sustitución incorrecta ejecutando $ {ls … | sed …} con bash

Recibí el siguiente error:

./assemblyDB.116.las test.sh: line 9: ${ls $filename | sed "s/assemblyDB.//" | sed "s/.las//"}: bad substitution 

y este es el script:

for filename in $(find . -type f -name "assemblyDB.*.las"); do echo $filename no=${ls $filename | sed "s/assemblyDB.//" | sed "s/.las//"} echo $no done 

Comentarios

Respuesta

${ ... } (llaves) marca varias tipos de expansión de parámetros , la más simple de las cuales es simplemente expandir el valor de una variable. El contenido entre llaves en su código no es un nombre de parámetro válido, o cualquier otra expansión, por lo que el shell se queja.

Parece que desea la sustitución de comandos en su lugar, para eso, la sintaxis es $( ... ) (paréntesis regular).

Además, el ls en ls $filename | sed... parece un poco innecesaria, la variable se expande a su nombre de archivo, y ls simplemente la pasa. Puede usar echo "$filename" | sed ... en su lugar.

Dicho esto, podrías hacer esas modificaciones directamente en el shell:

no="${filename/assemblyDB.}" # remove first match no="${no/.las}" 

o, usando los operadores estándar:

no="${filename#assemblyDB.}" # remove from start of string no="${no%.las}" # remove from end of string 

Si ejecuta sed, es posible que desee tener en cuenta que . coincide con cualquier carácter en expresiones, por lo que sería más correcto citarlo con una barra invertida. También puede dar una sed instancia ambos comandos: sed -e "s/assemblyDB\.//" -e "s/\.las//".

Y luego for filename in $(find . -type f -name "assemblyDB.*.las"); do tiene todos los problemas que tiene al analizar ls , principalmente el hecho de que los espacios en blanco y los comodines en los nombres de archivo lo romperán. En ksh / Bash / zsh, podría hacer todo ese ciclo en el shell:

shopt -s globstar # in Bash for filename in **/assemblyDB.*.las; do ... 

Comentarios

  • @Jesse_b, sí, pero si su shell tiene <<<, probablemente también tenga ${var/pat/repl}. Si ejecuta sed, simplemente podría asignar ambas reglas a una sed instancia: sed -e 's/assemblyDB\.//' -e 's/\.las//'
  • Solo quería señalar UUOE;)

Responder

En lugar de ${} use comillas inversas ` [El botón debajo de Escape en su teclado.]

Comentarios

  • Por favor, no ' t use comillas invertidas. Utilice la sustitución de comandos. $( ... )
  • Están desactualizados, son difíciles de leer, no ' t anidan y pueden siempre ser reemplazado por la sustitución de comandos entre paréntesis. Además, son difíciles de colocar en bloques de código en línea en la mayoría de los lenguajes de rebajas 🙂
  • Sí, es cierto. Pero en lo que respecta a esta pregunta, ¿dónde salió mal mi respuesta?
  • Sugirió usar comillas invertidas.
  • @Jesse_b Hay ' muchos de las razones para no usar comillas inversas, pero la incapacidad de anidarlas no es una de ellas: echo `echo \`echo hello\`` . Sí, requiere escapar, pero eso solo significa que ' no es tan fácil de anidar como $(...), no es que ' es imposible.

Responder

Sustitución de comandos (línea 3)

for filename in $(find . -type f -name "assemblyDB.*.las"); do echo $filename no=$(ls $filename | sed "s/assemblyDB.//" | sed "s/.las//") echo $no done 

es equivalente, como ya se señaló, a (línea 3, no ls)

for filename in $(find . -type f -name "assemblyDB.*.las"); do echo $filename no=$(echo $filename | sed "s/assemblyDB.//" | sed "s/.las//") echo $no done 

o más corto (línea 4 desaparecida, ahora echo directamente)

for filename in $(find . -type f -name "assemblyDB.*.las"); do echo $filename echo $filename | sed "s/assemblyDB.//" | sed "s/.las//" done 

y el comando sed se puede reducir a (línea 3)

for filename in $(find . -type f -name "assemblyDB.*.las"); do echo $filename echo $filename | sed "s/assemblyDB.//;s/.las//" done 

o tal vez extraiga la parte del medio con sed: (línea fija 3)

for filename in $(find . -type f -name "assemblyDB.*.las"); do echo $filename echo $filename | sed -r "s/.*assemblyDB.(.*).las/\1/"" done 

ahora find-iterator en lugar de for-iterator: (línea 1 a 3, 4 desaparecida)

find . -type f -name "assemblyDB.*.las" -printf "%f\n" -exec sh -c " echo {} | sed -r "s/.*assemblyDB.(.*).las/\1/"" ";" assemblyDB.11.las 11 assemblyDB.9.las 9 assemblyDB.10.las 10 

Si sus nombres de archivo están en tal o un orden similar, lo siguiente también podría funcionar:

for i in {8..12} ; do ls assemblyDB.$i.las && echo $i ; done 2>/dev/null assemblyDB.9.las 9 assemblyDB.10.las 10 assemblyDB.11.las 11 

(8 y 12 incluidos, para demostrar los archivos que faltan en la secuencia).

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *