Баш: найти |сед |xargs rm не работает, но rm делает - PullRequest
1 голос
/ 10 июля 2019

Я пытаюсь удалить все файлы .js и .js.map из любого подкаталога src, называемого __tests__.

$ find . -path './src/**' -name __tests__ | # find subdirectories
> sed -E 's/([^ ]+__tests__)/\1\/*.js \1\/*.js.map/g' | # for each subdirectory, concat *.js and *.js.map
> xargs rm # remove files

. Это происходит со следующими ошибками:

rm: cannot remove './src/game/__tests__/*.js': No such file or directory
rm: cannot remove './src/game/__tests__/*.js.map': No such file or directory
rm: cannot remove './src/helpers/__tests__/*.js': No such file or directory
rm: cannot remove './src/helpers/__tests__/*.js.map': No such file or directory

Однако, если я изменил xargs rm на xargs echo rm, скопировал и вставил вывод и запустил его, он работает.

$ find . -path './src/**' -name __tests__ | sed -E 's/([^ ]+__tests__)/\1\/*.js \1\/*.js.map/g' |
> xargs echo rm # echo command to remove files
rm ./src/game/__tests__/*.js ./src/game/__tests__/*.js.map ./src/helpers/__tests__/*.js ./src/helpers/__tests__/*.js.map

$ rm ./src/game/__tests__/*.js ./src/game/__tests__/*.js.map ./src/helpers/__tests__/*.js ./src/helpers/__tests__/*.js.map

Завершение вывода моего эха в$(...) и добавление rm приводит к той же ошибке, что и раньше.

$ rm $(find . -path './src/**' -name __tests__ | sed -E 's/([^ ]+__tests__)/\1\/*.js \1\/*.js.map/g' | xargs echo rm
rm: cannot remove './src/game/__tests__/*.js': No such file or directory
rm: cannot remove './src/game/__tests__/*.js.map': No such file or directory
rm: cannot remove './src/helpers/__tests__/*.js': No such file or directory
rm: cannot remove './src/helpers/__tests__/*.js.map': No such file or directory

Что я делаю не так?

Я сомневаюсь, что это важно, но я использую GitBash в Windows.

Ответы [ 2 ]

2 голосов
/ 11 июля 2019

Во-первых, чтобы объяснить проблему: в find | sed | xargs rm оболочка только устанавливает связь между этими программами, но фактически не обрабатывает результаты. Это проблема здесь, потому что *.js необходимо расширить оболочкой , чтобы заменить ее списком имен файлов; rm обрабатывает каждый заданный аргумент как буквальное имя. (Это не похоже на Windows, где программы выполняют собственный анализ командной строки и расширение glob).

Возможно, вам вообще не нужно find здесь. Рассмотрим:

shopt -s globstar                 # enable ** as a recursion operator
rm ./src/**/__tests__/*.js{,.map} # delete *.js and *.js.map in any __tests__ directory under src

... или, если вы do хотите использовать find, дайте ему возможность составить список отдельных файлов, соответствующих *.js, вместо того, чтобы оставить эту работу выполненной позже:

find src -regextype posix-egrep -regex '.*/__tests__/[^/]*[.]js([.]map)?' -delete
1 голос
/ 11 июля 2019

Вам нужно расширить свои шары (*).Расширение имени файла выполняется оболочкой в ​​UNIX, а не rm или другими программами.Попробуйте:

.... | xargs -d $'\n' sh -c 'IFS=; for f; do rm -- $f; done' sh

..., чтобы объяснить это:

  • -d $'\n' гарантирует, что xargs расщепляется только на новых строках (не пробелах!), А также останавливает его обработкуобратные косые черты и кавычки как специальные.
  • sh -c '...' sh запускает ... как скрипт, с sh как $0 и последующими аргументами в $1 и т.д .;Таким образом, for f; будет перебирать эти аргументы.
  • Очистка IFS с помощью IFS= предотвращает расщепление строк, когда $f используется без кавычек, поэтому происходит только расширение глобуса.
  • Использование аргумента -- для rm гарантирует, что последующие аргументы будут обрабатываться как имена файлов, а не опции, даже если они начинаются с тире.

Тем не менее, если у вас действительно много файлов дляВ каждом шаблоне вы можете столкнуться с «слишком длинным списком аргументов», даже если вы используете xargs.


Другое предостережение заключается в том, что имена файлов, содержащие символы новой строки, потенциально могут быть разбиты на несколько имен (в зависимости от деталейверсии find, которую вы используете).Способ решения этой проблемы, который будет работать со всеми POSIX-совместимыми версиями find, может быть следующим:

find ./src -type d -name __tests__ -exec sh -c '
  for d; do
    rm -- "$d"/*.js{,.map}
  done
' sh {} +
...