Estou executando o seguinte script no Ubuntu 22.04:
#!/bin/bash
logfile="/absolute/path/to/log.txt"
find /absolute/path/to/haystack -type d -name "needle" -execdir sh -c 'pwd > $logfile' \;
A esperança é que ele imprima o caminho completo do diretório de cada pasta nomeada needle/
dentro da pasta haystack/
para o arquivo log.txt
. Em vez disso, ele imprime nada além de uma longa lista do erro sh: 1: cannot create : Directory nonexistent
para a saída padrão (e não grava nada em log.txt
).
Esta resposta demonstra que o erro se origina no Dash, não no Bash. Como estou usando o Bash shebang, isso significa que o erro deve ser originado do uso de-execdir sh
-execdir sh -c 'pwd > $logfile' \;
já que no Ubuntu sh
é symlinked para Dash. Minha primeira teoria foi que o Bash não está passando sua variável $logfile
para Dash. Esta resposta afirma que as variáveis são copiadas para subshells, o que foi meu entendimento, mas talvez isso não seja verdade em shells completamente diferentes. Para testar, alterei o shebang para #!/bin/sh
, que é Dash neste sistema. O resultado é o mesmo, no entanto: linhas do erro sh: 1: cannot create : Directory nonexistent
.
O que causa esse erro?
Qualquer ajuda é bem-vinda, mas observe que postei porque quero entender a origem do erro, assim como a pergunta pede. Não estou interessado em nenhuma resposta que tenha uma abordagem diferente para listar needle/
diretórios, a menos que essa resposta seja acompanhada por uma explicação do motivo pelo qual esse script falha.
No seu roteiro...
A variável
logfile
não está definida dentro do-execdir
script:Seu script é escrito entre aspas simples (
sh -c 'pwd > $logfile'
), então a variável não é expandida pelo shell pai.logfile
não é uma variável de ambiente, portanto não está disponível no shell filho.Se você usar aspas duplas, deverá funcionar como esperado:
Dito isso, não está claro por que você está usando
-execdir
here; você poderia fazer a mesma coisa usando-printf
, assim:Como larsks disse, como
'pwd > $logfile'
está entre aspas simples, ele é passado para o shell secundário como uma string literal:
ou seja, 14 caracteres separados e comuns. O fato dep
w
d
>
$
l
o
g
f
i
l
e
logfile
ser uma variável no shell externo não importa (por causa das aspas simples).O shell secundário tentará então expandir
$logfile
e substituirá por nada (porque não está definido naquele shell), e então tentará executarpwd >
(sem nome de arquivo para o redirecionamento). É isso que está causando o erro que você está recebendo.Como outros disseram, o shell secundário não vê a
logfile
variável porque ela não é exportada. Uma correção rápida para seus comandos éObserve que você deve sempre citar variáveis de shell, a menos que tenha um bom motivo para não fazê-lo e tenha certeza de que sabe o que está fazendo.
O parágrafo a seguir foi incluído na tentativa de ser completo, mas pode confundir você:
Como ilkkachu disse , já que toda a saída vai para um único arquivo (especificado por um caminho absoluto), você não precisa redirecionar a saída em cada execução de comando. Você poderia fazer
(aplicando o redirecionamento uma vez, para todo o
find
comando).Mas, se você não estiver redirecionando a saída em cada execução de comando, você não precisa necessariamente invocar explicitamente o shell. Tente
No entanto, isso fará com que
find
seja executado/bin/pwd
(ou/usr/bin/pwd
ou qualquer coisa), o que pode produzir resultados diferentes desh -c 'pwd'
, já quepwd
é um comando interno do shell.