substituição incorreta executando $ {ls … | sed …} com bash

Recebi o seguinte erro:

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

e este é o 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 

Comentários

Resposta

${ ... } (colchetes) marca vários tipos de expansão de parâmetro , a mais simples delas é apenas expandir o valor de uma variável. As coisas entre colchetes em seu código não são “um nome de parâmetro válido, ou qualquer outra expansão, então o shell reclama.

Você parece querer a substituição do comando, para isso, a sintaxe é $( ... ) (parênteses regulares).

Além disso, o ls em ls $filename | sed... parece um pouco desnecessário, a variável se expande para o seu nome de arquivo e ls apenas o transmite. Você pode simplesmente usar echo "$filename" | sed ... em vez disso.

Dito isso, você pode fazer essas modificações diretamente no shell:

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

ou, usando os operadores padrão:

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

Se você executar sed, talvez queira observar que . corresponde a qualquer caractere em expressões, então seria mais correto citá-lo com uma barra invertida. Além disso, você pode dar a uma sed instância os dois comandos: sed -e "s/assemblyDB\.//" -e "s/\.las//".

E então for filename in $(find . -type f -name "assemblyDB.*.las"); do tem todos os problemas análise de ls tem, principalmente o fato de que espaços em branco e curingas em nomes de arquivo podem quebrá-lo. Em ksh / Bash / zsh, você pode fazer todo esse loop no shell:

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

Comentários

  • @Jesse_b, sim, mas se seu shell tiver <<<, provavelmente também terá ${var/pat/repl}. Se você executar o sed, poderá apenas fornecer as duas regras a uma sed instância: sed -e 's/assemblyDB\.//' -e 's/\.las//'
  • Só desejo aponte UUOE;)

Resposta

Em vez de ${} use backticks ` [O botão abaixo de Escape em seu teclado.]

Comentários

  • Por favor, não ' t use crases. Use a substituição de comando. $( ... )
  • Eles estão desatualizados, são difíceis de ler, não ' t aninhados e podem sempre ser substituído por uma substituição de comando entre parênteses. Além disso, eles são difíceis de colocar em blocos de código embutidos na maioria das linguagens de markdown 🙂
  • Sim, isso mesmo. Mas com relação a essa pergunta, onde minha resposta deu errado ??
  • Você sugeriu usar crases.
  • @Jesse_b Há ' muitos de razões para não usar crases, mas a incapacidade de aninhá-los não é uma delas: echo `echo \`echo hello\`` . Sim, requer escape, mas isso significa apenas que ' não é tão fácil de aninhar como $(...), não que ' é impossível.

Resposta

Substituição de comando (linha 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 

é equivalente, como já apontado, a (linha 3, não 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 

ou mais curto (linha 4 sumida, agora echo diretamente)

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

e o comando sed pode ser reduzido para (linha 3)

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

ou talvez extraia a parte do meio com sed: (ainda linha 3)

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

agora find-iterator em vez de for-iterator: (linha 1 a 3, 4 desaparecido)

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 

Se seus nomes de arquivo estiverem em tal ou uma ordem semelhante, o seguinte pode funcionar também:

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 e 12 incluídos, para demonstrar arquivos ausentes na sequência).

Deixe uma resposta

O seu endereço de email não será publicado. Campos obrigatórios marcados com *