Estou usando o seguinte código para concatenar todos os arquivos pdf no diretório atual:
find . -iname '*.pdf'|sort|xargs|xargs -I {} pdftk {} cat output union.pdf
A primeira invocação de xargs tem o efeito de converter a saída de sort em uma única linha, com itens separados por um espaço. Mas o resultado é este:
Error: Unable to find file.
Error: Failed to open PDF file:
./001.pdf ./002.pdf ./003.pdf ./004.pdf ./007.pdf ./010.pdf ./031.pdf ./057.pdf ./077.pdf ./103.pdf ./131.pdf ./155.pdf ./179.pdf ./205.pdf ./233.pdf ./261.pdf ./285.pdf ./313.pdf ./331.pdf ./357.pdf ./383.pdf ./411.pdf
Errors encountered. No output created.
Done. Input errors, so no output created.
O xargs passa o argumento para pdftk com aspas ao redor? Como evitar isso? (Espaços em branco, escape e a forma como eles interagem com os comandos sempre me deixam louco...)
Sim e não, mas tecnicamente não. xargs não faz citações e pdftk também não tira aspas.
A maneira como os programas recebem argumentos de linha de comando no Linux/Unix não é usando uma única string que precisa ser citada e não citada – é assim que funciona a linguagem de "shell de comando" voltada para o usuário, e as aspas são interpretadas pelo seu shell, não pelos próprios programas. (Isso é o oposto de como o Windows faz isso.)
Internamente, os programas são iniciados usando uma matriz (/lista/vetor) de strings, que preserva inerentemente o conteúdo exato do texto e a separação de cada elemento, portanto, realmente não usa aspas ou escape em primeiro lugar. (Isto é - a menos que você tenha que aninhá-lo, caso em que está de volta à citação e análise de strings, como você verá abaixo ...)
Por exemplo, sua linha de comando é analisada para isso (usando sintaxe de array semelhante a C, por exemplo, mas as aspas não fazem parte das strings):
Portanto, quando xargs lê uma linha de entrada (porque -I define o modo linha por linha), ele substitui os símbolos
{}
em cada elemento individual pela linha de entrada, sem reorganizar os elementos de forma alguma. Em seguida, ele pede ao sistema operacional para executar o resultado:Portanto, você precisará de uma maneira diferente de conseguir isso
xargs -I
sozinho.Você poderia, por exemplo, pedir ao xargs para executar um shell - que irá então interpretar/dividir/retirar aspas da entrada da mesma forma que você esperaria de um shell:
O elemento seguinte -c se tornará
pdftk ./001.pdf ./002.pdf … cat output union.pdf
e o bash o dividirá em palavras conforme o esperado. (Mas observe que, como xargs não faz aspas, isso dividirá os nomes de arquivos que contêm espaços e fornecerá resultados estranhos quando os nomes de arquivos contiverem caracteres especiais.)Você pode usar o recurso de "substituição de processo" do shell:
Isso dividirá o texto resultante em qualquer espaço em branco (assim como
$var
a expansão variável). As linhas não precisam ser unidas primeiro. Mas terá os mesmos problemas com nomes de arquivos contendo espaços e um pouco menos com caracteres especiais.Recomendado: você pode evitar 'find' e 'xargs' totalmente e usar a correspondência de curinga integrada do shell interativo diretamente:
Comum * não é recursivo, mas no Bash ou zsh você também tem ** que é o modo recursivo:
(Os resultados da correspondência sempre serão classificados, pelo menos em shells usando a linguagem POSIX sh. E como o shell expande diretamente cada nome de arquivo para um elemento de linha de comando individual, não haverá nenhum problema de citação, mesmo com nomes de arquivo incomuns.)