substituição ruim rodando $ {ls… | sed…} com o bash

5

Eu 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
    
por user977828 25.02.2018 / 14:58

3 respostas

18

${ ... } (chaves) marca vários tipos de expansão de parâmetros , a mais simples das quais é apenas expandir o valor de uma variável. O material entre chaves não é um nome de parâmetro válido, ou qualquer outra expansão, então o shell reclama.

Parece que você quer a substituição de comandos, por isso, a sintaxe é $( ... ) (parênteses regulares).

Além disso, o ls em ls $filename | sed... parece um pouco desnecessário, a variável é expandida para o nome do arquivo e ls apenas a passa. Você poderia usar apenas echo "$filename" | sed ... .

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 , convém observar que . corresponde a qualquer caractere em expressões regulares, por isso seria mais correto citá-lo com uma barra invertida. Além disso, você pode dar a um sed instance ambos os comandos: sed -e 's/assemblyDB\.//' -e 's/\.las//' .

E, em seguida, for filename in $(find . -type f -name "assemblyDB.*.las"); do tem os problemas de analisar ls , principalmente o fato de que espaços em branco e curingas nos nomes dos arquivos vão quebrar isto. No ksh / Bash / zsh, você poderia fazer todo esse loop no shell:

shopt -s globstar         # in Bash
for filename in **/assemblyDB.*.las; do
    ...
    
por 25.02.2018 / 15:08
0

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á foi apontado, para (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 menor (linha 4 desaparecida, agora ecoando 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//'" 
done

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

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

assemblyDB.11.las
11
assemblyDB.9.las
9
assemblyDB.10.las
10

Se os seus nomes de arquivos estiverem em uma ordem semelhante ou semelhante, os seguintes itens também funcionarão:

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).

    
por 26.02.2018 / 03:46
-3

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

    
por 25.02.2018 / 15:04

Tags