scope variável dentro de pipes?

1

isso é super trivial e eu encontrei muitas respostas próximas, mas ainda não consigo resolver isso. Eu fiz um script com um argumento, basicamente algo como:

var1=$1
echo "outside the pipe, my variable is set: $var1"
ls *.* | xargs sh -c 'echo "$0" "$@"; echo "inside the pipe, my variable is empty: $var1"'

meu problema é que eu preciso canalizar uma lista de nomes de arquivos e também minha variável. Como eu faria isso? Eu tentei escrever minha lista de arquivos em uma matriz, em seguida, acrescentar / prefixar meu argumento e passar isso para o pipe, mas isso cria problemas mais tarde, já que eu processo isso em blocos, meu script original é algo como (imagemagick):

ls *.png | xargs -n 100 sh -c 'convert "$0" "$@" -evaluate-sequence $var1 ../out2/"$0"'

qualquer ajuda é muito apreciada!

    
por user3647558 14.07.2018 / 20:43

3 respostas

0

Você não precisa executar outro shell para fazer isso. Para lidar com todos os arquivos que correspondem à expressão glob, basta usar o glob na linha de comando do programa que você deseja executar no final:

convert *.png -whatever /some/output/path

Se você precisar acessar apenas parte dos arquivos, armazene-os em uma matriz e divida-os ou indexe-os:

files=(*.png)
convert "${files[0]}" "${files[@]:1}" -evaluate-sequence "$var1" ../out2/"${files[0]}"

( "${files[0]}" é o primeiro nome do arquivo, "${files[@]:1}" se expande para todos os outros)

De lá, podemos processar os arquivos cem por vez:

for (( i=0; i < "${#files[@]}" ; i+=100 )); do 
    convert "${files[@]:i:100}" -evaluate-sequence "$var1" ../out2/"${files[i]}"
done

Isto é, se eu interpretei o comando xargs corretamente. Parece passar o primeiro nome de arquivo como o argumento zeroth para sh -c (geralmente como o nome do shell).

Mas, para responder à pergunta que você pediu, você pode export da variável para torná-la visível para o shell interno ou colocá-la entre aspas duplas para que ela seja expandida pelo shell externo. Por exemplo.

var=foo
export var
echo bar | xargs sh -c 'echo "$var" "$1"' sh

ou

var=foo
echo bar | xargs sh -c "echo '$var' "'"$1"' sh

Ambas as saídas foo bar . Passar pelo ambiente é melhor porque a sintaxe é mais limpa (observe os diferentes tipos de aspas em torno de $var e $1 no último), e quaisquer citações no valor da variável não causarão problemas, como no segundo caso.

    
por 14.07.2018 / 21:55
1

Não, você não precisa canalizar uma lista de nomes de arquivos.

var1=$1

find . -maxdepth 1 -type f -name '*.png' -exec sh -c '
    var1=$1; first=$2; shift 2
    convert "$first" "$@" -evaluate-sequence "$var1" ../out2/"$first"' sh "$var1" {} +

Ou seja, dê ao shell filho interno $var1 como o primeiro argumento (o argumento zeroth deve ser o nome do shell).

O shell interno tira $var1 da lista de parâmetros, e também faz o mesmo para o primeiro nome de caminho que find encontra (não sei por que, mas isso pelo menos faz com que seja equivalente ao que eu acredito que você código faz) e, em seguida, desliga esses de $@ usando shift 2 .

Se você precisar fazer isso em lotes de exatamente 100:

var1=$1

find . -maxdepth 1 -type f -name '*.png' -print0 |
xargs -0 -n 100 sh -c '
    var1=$1; first=$2; shift 2
    convert "$first" "$@" -evaluate-sequence "$var1" ../out2/"$first"' sh "$var1"

A razão pela qual isso é mais seguro do que canalizar a saída de ls é porque os nomes de caminho são passados como uma lista terminada em nul em vez de uma lista terminada por nova linha. O script sh é o mesmo em ambas as variações e aceita $var1 como um argumento de linha de comando.

O motivo pelo qual seu código não funciona é porque o shell sh -c não sabe $var1 de seu shell pai.

Relacionados:

por 14.07.2018 / 21:15
0

O sh -c é um novo shell, então você precisa fazer um export de $var1 para que os filhos o processem ou passem para sh -c como argumento.

O outro problema com o seu exemplo é que você tem a variável dentro de carrapatos individuais.

    
por 14.07.2018 / 21:16