¿Cómo extraer los dos primeros caracteres de una cadena en el script de shell?

123

Por ejemplo, dado:

USCAGoleta9311734.5021-120.1287855805

Quiero extraer solo:

US
Greg
fuente
66
Gracias a todos. Terminé usando 'cut -c1-2', honestamente, ni siquiera sabía que 'cut' estaba allí. Me gustaría decir que tengo bastante experiencia en la línea de comandos, pero aparentemente tengo mucho que aprender.
Greg
1
@ Greg, solo ten en cuenta que el corte se ejecuta como un proceso separado: será más lento que la solución de bash interno que publiqué junto a él en mi respuesta. Eso no hará ninguna diferencia a menos que esté procesando grandes conjuntos de datos, pero debe tenerlo en cuenta.
paxdiablo
Editar En realidad, creo que esta línea de código probablemente se ejecutará unas 50,000 veces por informe. Por lo tanto, podría seguir con el método interno de Bash, que como dijiste ahorrará algunos recursos muy necesarios.
Greg
relacionado: unix.stackexchange.com/questions/3454/…
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Respuestas:

180

Probablemente el método más eficiente, si está utilizando el bashshell (y parece que lo es, según sus comentarios), es usar la variante de subcadena de expansión de parámetros:

pax> long="USCAGol.blah.blah.blah"
pax> short="${long:0:2}" ; echo "${short}"
US

Esto se establecerá shortpara ser los dos primeros caracteres de long. Si longes más corto que dos caracteres, shortserá idéntico a él.

Este método de shell generalmente es mejor si lo vas a hacer mucho (como 50,000 veces por informe como mencionas) ya que no hay sobrecarga de creación de procesos. Todas las soluciones que utilizan programas externos sufrirán esa sobrecarga.

Si también quisieras asegurar una longitud mínima , puedes rellenarlo de antemano con algo como:

pax> long="A"
pax> tmpstr="${long}.."
pax> short="${tmpstr:0:2}" ; echo "${short}"
A.

Esto garantizaría que algo menos de dos caracteres de longitud se rellenara a la derecha con puntos (o algo más, simplemente cambiando el carácter utilizado al crear tmpstr). No está claro que necesites esto, pero pensé que lo pondría completo.


Dicho esto, hay varias maneras de hacer esto con programas externos (por ejemplo, si no tiene a su bashdisposición), algunas de las cuales son:

short=$(echo "${long}" | cut -c1-2)
short=$(echo "${long}" | head -c2)
short=$(echo "${long}" | awk '{print substr ($0, 0, 2)}'
short=$(echo "${long}" | sed 's/^\(..\).*/\1/')

Los primeros dos ( cuty head) son idénticos para una cadena de una sola línea; básicamente, ambos solo le devuelven los dos primeros caracteres. Se diferencian en que cutle dará los dos primeros caracteres de cada línea yhead le dará los dos primeros caracteres de toda la entrada.

El tercero usa la awkfunción de subcadena para extraer los dos primeros caracteres y el cuarto usa los sedgrupos de captura (usando ()y \1) para capturar los dos primeros caracteres y reemplazar toda la línea con ellos. Ambos son similares a cut: entregan los dos primeros caracteres de cada línea en la entrada.

Nada de eso importa si está seguro de que su entrada es una sola línea, todas tienen un efecto idéntico.

paxdiablo
fuente
Prefiero usar printf '%s'en lugar de echoen el caso de que haya caracteres extraños en la cadena: stackoverflow.com/a/40423558/895245 Para el POSIX obsesionado: head -cno es POSIX, cut -cy awk substrson, sed \1no estoy seguro.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
1
@CiroSantilli 新疆 改造 中心 996ICU 六四 事件 usando printf, ni siquiera necesita un programa adicional. Mira mi respuesta .
bschlueter
60

la forma más fácil es

${string:position:length}

Donde esto extrae la $lengthsubcadena de $stringat$position .

Este es un bash incorporado así que no se requiere awk o sed.

ennuikiller
fuente
Esta es la forma corta, dulce y fácil de obtener la subcadena.
ani627
34

Usted ha conseguido varias respuestas buenas y me gustaría ir con el Bash incorporado a mí mismo, pero ya que preguntas acerca sedy awky ( casi soluciones) que nadie más ofrece en base a ellas, les ofrecen éstas:

echo "USCAGoleta9311734.5021-120.1287855805" | awk '{print substr($0,0,2)}'

y

echo "USCAGoleta9311734.5021-120.1287855805" | sed 's/\(^..\).*/\1/'

El awkuno debería ser bastante obvio, pero aquí hay una explicación sed:

  • sustituto "s /"
  • el grupo "()" de dos de los caracteres ".." que comienza al principio de la línea "^" y seguido de cualquier carácter "." repetido cero o más veces "*" (las barras invertidas son necesarias para escapar de algunos de los caracteres especiales)
  • por "/" el contenido del primer (y único, en este caso) grupo (aquí la barra invertida es un escape especial que se refiere a una sub-expresión coincidente)
  • hecho "/"
Pausado hasta nuevo aviso.
fuente
1
En awk las cadenas comienzan en el índice 1, por lo que debe usar substr($0,1,2).
Isaac
8

Si estás dentro bash, puedes decir:

bash-3.2$ var=abcd
bash-3.2$ echo ${var:0:2}
ab

Esto puede ser justo lo que necesitas ...

Dominic Mitchell
fuente
¡Es la respuesta más fácil y más simple! funcionó como un encanto
aloha
7

Solo grep:

echo 'abcdef' | grep -Po "^.."        # ab
Amir Mehler
fuente
Se adapta a mis necesidades. Puede eliminar la -Popción para acortarla. Todas las expresiones regulares entenderán ese patrón.
datashaman
6

Puedes usar printf:

$ original='USCAGoleta9311734.5021-120.1287855805'
$ printf '%-.2s' "$original"
US
bschlueter
fuente
5

colrm - elimina columnas de un archivo

Para dejar los dos primeros caracteres, simplemente elimine las columnas a partir de 3

cat file | colrm 3
Ian Yang
fuente
4

Muy tarde, pero aquí está

sed 's/.//3g'

O

awk NF=1 FPAT=..

O

perl -pe '$_=unpack a2'
Steven Penny
fuente
2

Si desea utilizar secuencias de comandos de shell y no confiar en extensiones que no son posix (como los llamados bashisms), puede utilizar técnicas que no requieren herramientas externas de forking como grep, sed, cut, awk, etc., que luego Haz que tu guión sea menos eficiente. Quizás la eficiencia y la portabilidad posix no sean importantes en su caso de uso. Pero en caso de que lo sea (o simplemente como un buen hábito), puede usar el siguiente método de opción de expansión de parámetros para extraer los dos primeros caracteres de una variable de shell:

$ sh -c 'var=abcde; echo "${var%${var#??}}"'
ab

Utiliza la expansión del parámetro "prefijo más pequeño" para eliminar los dos primeros caracteres (esta es la ${var#??}parte), luego la expansión del parámetro "sufijo más pequeño" (el${var% parte) para eliminar la cadena de todo menos los dos primeros caracteres del original valor.

Este método se describió previamente en esta respuesta a la pregunta "Shell = Verificar si la variable comienza con #". Esa respuesta también describe un par de métodos de expansión de parámetros similares que se pueden usar en un contexto ligeramente diferente al que se aplica a la pregunta original aquí.

Juan
fuente
La mejor respuesta, debería estar en la cima. sin tenedores, sin bashisms. funciona incluso con conchas pequeñas como el tablero.
exore
1

Si su sistema está usando un shell diferente (no bash), pero su sistema sí bash, aún puede usar la manipulación de cadena inherente bashinvocando bashcon una variable:

strEcho='echo ${str:0:2}' # '${str:2}' if you want to skip the first two characters and keep the rest
bash -c "str=\"$strFull\";$strEcho;"
palswim
fuente
Utiliza el mismo método que la respuesta principal , solo invoca bashsi aún no lo está utilizando.
palswim
Desafortunadamente, esto viene con toda la sobrecarga de invocar otro proceso, pero a veces esa sobrecarga no importa tanto como la simplicidad y la familiaridad.
palswim
1

Solo por diversión, agregaré algunos que, aunque son demasiado complicados e inútiles, no se mencionaron:

head -c 2 <( echo 'USCAGoleta9311734.5021-120.1287855805')

echo 'USCAGoleta9311734.5021-120.1287855805' | dd bs=2 count=1 status=none

sed -e 's/^\(.\{2\}\).*/\1/;' <( echo 'USCAGoleta9311734.5021-120.1287855805')

cut -c 1-2 <( echo 'USCAGoleta9311734.5021-120.1287855805')

python -c "print(r'USCAGoleta9311734.5021-120.1287855805'[0:2])"

ruby -e 'puts "USCAGoleta9311734.5021-120.1287855805"[0..1]'
Matias Barrios
fuente
0
perl -ple 's/^(..).*/$1/'
dsm
fuente
Olvidaste hacer eco de la cuerda en eso.
Chas. Owens
0

si mystring = USCAGoleta9311734.5021-120.1287855805

print substr(mystring,0,2)

nos imprimiría

donde 0 es la posición de inicio y 2 es cómo muchos caracteres para leer

Jambobond
fuente
Di ... ¿no es eso GW-BASIC? Oh, espera, eso es awk. Lo siento, no pude saber al principio.
Pausado hasta nuevo aviso.
0

¿Es esto lo que buscas?

my $string = 'USCAGoleta9311734.5021-120.1287855805';

my $first_two_chars = substr $string, 0, 2;

ref: substr

draegtun
fuente
1
dado que es probable que él / ella esté llamando esto desde la cáscara, una mejor forma seríaperl -e 'print substr $ARGV[0], 0, 2' 'USCAGoleta9311734.5021-120.1287855805'
Chas. Owens