Por que o xargs on find não funciona com uma série de comandos usando o pushd e o popd para percorrer uma subárvore de diretórios?

1

Em uma máquina Linux Centos 4, estou tentando criar uma linha de comando bash simples para percorrer uma estrutura de diretório abaixo de um diretório atual arbitrário e em cada subdiretório tocar em um arquivo, listar o conteúdo do diretório mas transportá-los para /dev/null , e remova o arquivo tocado. O ponto obscuro deste script é fazer cócegas no sistema cliente / servidor NFS subjacente para garantir que o conteúdo de cada diretório reflita uma alteração feita em uma máquina diferente que, de outra forma, levaria algum tempo para ser propagada. Eu encontrei esta solução alternativa evita o atraso. Ignorando os méritos da minha razão para fazer isso, por que o meu roteiro proposto não funciona?

[CentosMachine] find . -type d -print0 | xargs -0 -I {} pushd {}; touch xYzZy.fixZ; ls &> /dev/null; rm -f xYzZy.fixZ; popd
xargs: pushd: No such file or directory
bash: popd: directory stack empty

O comando find está retornando no momento:

.
./dir
./emptyDir
./dirOfDir
./dirOfDir/ofDir
./dirOfDir/ofDir/Dir(empty)

Primeiro, achei que talvez o ( e ) em um dos nomes de diretório fosse o problema, mas renomear esse diretório como ./dirOfDir/ofDir/Dir_empty_ não alterou o sintoma. Eu também tentei ver strace output, mas não vi nada que ajudou, mas vi os diretórios sendo processados.

Aqui está um trecho do final da saída strace com esse diretório renomeado para usar sublinhados em vez de parênteses:

[...]
chdir("ofDir")                          = 0
lstat64(".", {st_mode=S_IFDIR|0775, st_size=4096, ...}) = 0
lstat64("Dir_empty_", {st_mode=S_IFDIR|0775, st_size=4096, ...}) = 0
open("Dir_empty_", O_RDONLY|O_NONBLOCK|O_LARGEFILE|O_DIRECTORY) = 4
fstat64(4, {st_mode=S_IFDIR|0775, st_size=4096, ...}) = 0
fcntl64(4, F_SETFD, FD_CLOEXEC)         = 0
getdents64(4, /* 2 entries */, 32768)   = 48
getdents64(4, /* 0 entries */, 32768)   = 0
close(4)                                = 0
chdir("Dir_empty_")                     = 0
lstat64(".", {st_mode=S_IFDIR|0775, st_size=4096, ...}) = 0
chdir("..")                             = 0
lstat64(".", {st_mode=S_IFDIR|0775, st_size=4096, ...}) = 0
chdir("..")                             = 0
lstat64(".", {st_mode=S_IFDIR|0775, st_size=4096, ...}) = 0
chdir("..")                             = 0
lstat64(".", {st_mode=S_IFDIR|0775, st_size=4096, ...}) = 0
fchdir(3)                               = 0
write(1, ".
[CentosMachine] find . -type d -print0 | xargs -0 -I {} pushd {}; touch xYzZy.fixZ; ls &> /dev/null; rm -f xYzZy.fixZ; popd
xargs: pushd: No such file or directory
bash: popd: directory stack empty
./dir
.
./dir
./emptyDir
./dirOfDir
./dirOfDir/ofDir
./dirOfDir/ofDir/Dir(empty)
./emptyDir
[...]
chdir("ofDir")                          = 0
lstat64(".", {st_mode=S_IFDIR|0775, st_size=4096, ...}) = 0
lstat64("Dir_empty_", {st_mode=S_IFDIR|0775, st_size=4096, ...}) = 0
open("Dir_empty_", O_RDONLY|O_NONBLOCK|O_LARGEFILE|O_DIRECTORY) = 4
fstat64(4, {st_mode=S_IFDIR|0775, st_size=4096, ...}) = 0
fcntl64(4, F_SETFD, FD_CLOEXEC)         = 0
getdents64(4, /* 2 entries */, 32768)   = 48
getdents64(4, /* 0 entries */, 32768)   = 0
close(4)                                = 0
chdir("Dir_empty_")                     = 0
lstat64(".", {st_mode=S_IFDIR|0775, st_size=4096, ...}) = 0
chdir("..")                             = 0
lstat64(".", {st_mode=S_IFDIR|0775, st_size=4096, ...}) = 0
chdir("..")                             = 0
lstat64(".", {st_mode=S_IFDIR|0775, st_size=4096, ...}) = 0
chdir("..")                             = 0
lstat64(".", {st_mode=S_IFDIR|0775, st_size=4096, ...}) = 0
fchdir(3)                               = 0
write(1, ".%pre%./dir%pre%./emptyDir%pre%./dirOfDir%pre%./"..., 75) = 75
exit_group(0)                      = ?
./dirOfDir%pre%./"..., 75) = 75 exit_group(0) = ?
    
por WilliamKF 16.05.2014 / 01:39

2 respostas

1

xargs é uma ferramenta muito útil para

  1. executando comandos com argumentos obtidos de uma fonte dinâmica e
  2. minimizando o número de invocações de comando construindo linhas de comando longas com vários argumentos.

Quando você não está fazendo o segundo (ou seja, executando um comando por argumento, como você está fazendo), xargs não é tão valioso; Existem outras maneiras de executar os comandos. Em particular, se a origem dos argumentos for find , você poderá usar a opção -exec :

find . -type d -exec bash -c 'pushd "{}" &> /dev/null; touch xYzZy.fixZ; ls &> /dev/null; rm -f xYzZy.fixZ; popd &> /dev/null'

Mas esta resposta, como a sua, invoca um processo de shell para cada diretório. E cada processo tem seu próprio ambiente de execução; Alterar o diretório de trabalho em um subprocesso não afeta o processo pai. Então você não precisa de pushd e popd . E você não precisa especificar bash ; O velho e simples sh serve. E, se você não estiver definindo horários de modificação, não precisará de touch ; um comando nulo redirecionado criará um arquivo. Assim, podemos reduzir o acima para:

find . -type d -exec sh -c 'cd "{}"; > xYzZy.fixZ; ls &> /dev/null; rm -f xYzZy.fixZ'

Para o seu caso de uso, isso pode não importar, mas em outras situações você pode querer executar os últimos três passos somente se o cd for bem-sucedido:

find . -type d -exec sh -c 'cd "{}" && { > xYzZy.fixZ; ls &> /dev/null; rm -f xYzZy.fixZ;}'

Observe que você precisa ter espaço em branco após o { e um ponto e vírgula antes do } .

    
por 19.07.2014 / 00:22
1

Encontrei minha resposta com essa pergunta de estouro de pilha . Coloque os vários comandos em um formato como este:

bash -c 'command1; command2; ...'

O que se aplica aqui é:

find . -type d -print0 | xargs -0 -I {} bash -c 'pushd "{}"; touch xYzZy.fixZ; ls &> /dev/null; rm -f xYzZy.fixZ; popd'

Observe a adição de aspas duplas em torno de pushd "{}" para que o diretório com ( e ) funcione corretamente. Sem isso, você recebe um erro:

bash: -c: line 0: syntax error near unexpected token '('
bash: -c: line 0: 'pushd ./dirOfDir/ofDir/Dir(empty) &> /dev/null; touch xYzZy.fixZ; ls &> /dev/null; rm -f xYzZy.fixZ; popd &> /dev/null'

No entanto, os pushed e popd também precisam de supressão para evitar a saída:

find . -type d -print0 | xargs -0 -I {} bash -c 'pushd "{}" &> /dev/null; touch xYzZy.fixZ; ls &> /dev/null; rm -f xYzZy.fixZ; popd &> /dev/null'
    
por 16.05.2014 / 03:17