1

На компьютере с Linux Centos 4 я пытаюсь создать простую командную строку bash, чтобы пройти структуру каталогов ниже произвольного текущего каталога и в каждом подкаталоге дотронуться до файла, вывести список содержимого каталога, но направить его в /dev/null и удалить тронутый файл. Непонятный смысл этого сценария - пометить базовую систему клиент / сервер NFS, чтобы убедиться, что содержимое каждого каталога отражает изменения, внесенные на другом компьютере, в противном случае для распространения может потребоваться некоторое время. Я нашел, что этот обходной путь избегает задержки. Не обращая внимания на достоинства моей причины сделать это, почему не работает мой предложенный сценарий bash?

[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

Команда find в настоящее время возвращает:

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

Сначала я подумал, что, возможно, проблема связана с ( и ) в одном из имен каталогов, но переименование этого каталога в ./dirOfDir/ofDir/Dir_empty_ не изменило симптом. Я также попытался просмотреть вывод данных strace но не увидел ничего, что помогло, но увидел, что каталоги обрабатываются.

Вот фрагмент конца вывода strace с этим каталогом, переименованным для использования подчеркивания вместо скобок:

[...]
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, ".\0./dir\0./emptyDir\0./dirOfDir\0./"..., 75) = 75
exit_group(0)                      = ?

2 ответа2

1

xargs - очень полезный инструмент для

  1. выполнение команд с аргументами, взятыми из динамического источника, и
  2. сведение к минимуму количества вызовов команд путем построения длинных командных строк с несколькими аргументами.

Когда вы не выполняете # 2 (то есть выполняете одну команду на аргумент, как вы это делаете), xargs не так важен ; Есть и другие способы выполнения команд.  В частности, если источником аргументов является find , вы можете использовать опцию -exec :

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

Но этот ответ, как и ваш, вызывает процесс оболочки для каждого каталога.  И каждый процесс имеет свою собственную среду выполнения; изменение рабочего каталога в подпроцессе не влияет на родительский процесс.  Так что вам не нужны pushd и popd .  И вам не нужно указывать bash ; обычный старый sh подойдет.  И, если вы не устанавливаете время модификации, вам не нужно touch ; перенаправленная пустая команда создаст файл.  Таким образом, мы можем уменьшить вышеупомянутое до:

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

Для вашего случая использования это может не иметь значения, но в других ситуациях вы можете захотеть выполнить последние три шага, только если cd успешно:

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

Обратите внимание, что вам нужно иметь пробел после { и точку с запятой перед } .

1

Я нашел свой ответ с этим вопросом переполнения стека. Поместите несколько команд в форму, подобную этой:

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

Который применяется здесь дает:

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

Обратите внимание на добавление двойных кавычек вокруг pushd "{}" чтобы каталог с ( и ) работал правильно. Без этого вы получите ошибку:

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'

Тем не менее, pushed и popd также необходимо подавление , чтобы избежать вывода:

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'

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .