processo de substituição para abrir uma lista de arquivos com um aplicativo

0

Desejo usar a substituição de processo para direcionar uma lista de arquivos (produzida, por exemplo, por ls ou find ) para um aplicativo específico para abrir / visualizar. Embora o piping dessa lista para xargs seja adequado para um script ou binário, essa ação falhará se o objeto de xargs for um alias de shell, conforme observado em outras perguntas neste site . [O aplicativo específico que tenho em mente é feh ]

Devido a essa limitação de xargs em relação aos aliases bash, estou tentando usar a substituição de processo no formato [script/binary/alias] <(find . -iname '*') . Essa construção fornece o efeito desejado se a lista de arquivos for direcionada para determinados comandos do shell, como cat (ou less , se um redirecionamento de entrada, < , for prefixado à instrução de substituição do processo); no entanto, ele falha notavelmente se a entrada (uma lista de caminhos para arquivos) for direcionada para um aplicativo (por exemplo, feh , gimp ) para abertura / visualização. O erro que acompanha essa falha, no caso em que feh é o destinatário da instrução de substituição do processo, é " feh: No loadable images specified. " O uso de um operador de redirecionamento de entrada na instrução de substituição do processo não atenua o problema (diferentemente do caso de outras comandos, por exemplo, less ).

Assim, minha pergunta é se a substituição do processo pode ser empregada para o propósito declarado (ou seja, abrir uma lista de arquivos) e, em caso afirmativo, qual a sintaxe apropriada. O requisito específico para compatibilidade com aliases de bash (especificados em ~/.bash_aliases ou um arquivo de configuração semelhante).

    
por user001 30.01.2015 / 07:10

2 respostas

2

Se o comando aceitar vários arquivos como argumentos (e não como o conteúdo é lido pelo canal), a simples substituição de comandos deve funcionar:

[script/binary/alias] $(find .)

Como alternativa, se o comando aceitar apenas um arquivo de cada vez, um loop for deve fazer o seguinte:

for file in $(find .); do [script/binary/alias] "$file"; done

Em ambos os casos, nomes de arquivos contendo espaços, tabulações, nova linha, caracteres curinga, causarão problemas; um loop while / read lida com isso:

find . | while IFS= read -r file; do [script/binary/alias] "$file"; done

Nomes de arquivo contendo novas linhas ainda causarão problemas; A saída delimitada por 0 find endereçará aqueles (aqui assumindo bash , zsh , ksh ):

find . -print0 | while IFS= read -rd '' file; do [script/binary/alias] "$file"; done

Em todos esses exemplos, find . -type f pode ser mais apropriado se você quiser apenas processar arquivos regulares.

    
por 30.01.2015 / 07:20
2

Se você quiser que aliases sejam expandidos após xargs , você pode fazer:

alias xargs='xargs '

No entanto, observe que apenas a primeira palavra após xargs está sujeita à expansão de alias e que pode ter efeitos inesperados se você tiver aliases para comandos padrão.

$ alias xargs='xargs ' a='echo test'
$ set -x
$ echo x | xargs a
+ echo x
+ xargs echo test
test x
$ echo x | xargs -r a
+ echo x
+ xargs -r a
xargs: a: No such file or directory

Você pode querer chamar seu alias de outra coisa e usar um comportamento padrão mais sensato para xargs . Por exemplo, com o GNU xargs :

alias axargs='xargs -r -d "\n" '

Agora, a substituição do processo se expande para um argumento: o nome de um arquivo que, para <(...) , contém a saída do comando (como uma transmissão ao vivo).

Com a saída de find , isso é útil apenas para comandos que aceitam como argumento um nome de arquivo esperado para conter uma lista de arquivos delimitada por nova linha.

feh é um deles:

feh -f <(find . -mtime -1 -type f -name '*.jpg')

Isso só funciona se os nomes de arquivo não contiverem caracteres de nova linha.

A implementação GNU de xargs tem uma opção -a para obter a lista de argumentos de um arquivo e combinada com -0 e a opção -print0 (ou -exec printf '%sfind' {} + ) para cmdB permite passar uma lista de nomes de arquivos de forma confiável:

xargs -r0a <(find ... -print0) feh....

Isso não ajuda em seu problema de alias.

Agora, se você quiser passar a saída de um comando como argumento (s) para outro comando, é aí que você usaria a substituição comando ao invés de processar substituição. Cuidado com algumas advertências embora.

cmdA "$(cmdB)"

passa a saída de cmdA , sem os caracteres de nova linha à direita como um argumento para * * /etc/passwd .jpg . Por exemplo, se você tiver três arquivos regulares (aqui com nomes incomuns embora perfeitamente válidos): foo.txt , <nl><nl> e <nl> (onde find . -type f significa um caractere de nova linha), a saída de cmdB ( ./* * /etc/passwd .jpg<nl>./foo.txt<nl>./<nl><nl><nl> ) será cmdA , então ./* * /etc/passwd .jpg<nl>./foo.txt<nl>./ receberá um argumento cmdA .

Isso provavelmente não é o que você quer. Você deseja que ./* * /etc/passwd receba cada nome de arquivo como argumentos separados: ./foo.txt , ./<nl><nl> e find .

Então, basicamente, você precisaria dividir a saída de cmdB nos nomes de arquivo individuais. Os shells POSIX têm um operador para isso, o operador split + glob que é invocado implicitamente quando você deixa uma substituição de comando (ou expansão de parâmetro ou expansão aritmética) sem aspas.

cmdA $(cmdB)

dividirá a saída de $IFS (sem os caracteres de nova linha à direita) nos caracteres ./* * /etc/passwd .jpg (por espaço padrão, guia e nova linha) e, então, cada palavra estará sujeita a globbing.

No nosso exemplo acima, não é isso que queremos. Isso dividiria ./* em * , /etc/passwd , .jpg e ./* e os * e find seriam adicionados à lista de arquivos no diretório atual.

O que queremos é dividir em caracteres de nova linha e não fazer parte da globbing. Isso é feito com:

IFS='
' # newline only
set -f
cmdA $(cmdB)

Isso ainda não funciona para nomes de arquivos contendo caracteres de nova linha. A saída de -print0 geralmente não é pós-processável, a menos que você use zsh ou você pode garantir que não haverá nomes de arquivo com caracteres de nova linha.

Agora, apenas "$(cmdB)" permite dividir em cmdB caracteres:

cmdA ${(0)"$(cmdB)"}

dividiria cmdA nas sequências de caracteres NUL. Ou:

IFS=$'
cmdA ${$(find...):?no file}
' # no need for set -f in zsh cmdA $(cmdB)

Outro problema com as abordagens de substituição de comandos é que se zsh falhar e / ou não produzir nada, zsh ainda será executado (sem argumento). Isso pode ser evitado com esta sintaxe (ainda com zsh ):

myalias ./**/*.jpg(.m-1)

Mas se você estiver usando find , geralmente não precisa ter todos os problemas, pois jpg suporta internamente a maior parte da funcionalidade de %code% . Por exemplo, você usaria:

alias xargs='xargs '

Para exibir os arquivos %code% modificados pela última vez nas últimas 24 horas.

    
por 30.01.2015 / 11:40