Выполнение рекурсивного поиска и замены на sed только изменяет первый файл - PullRequest
2 голосов
/ 14 января 2020

Я пытаюсь рекурсивно искать текущий каталог, выполняя sed замену в первой строке каждого найденного файла .txt.

Выполнение любой из этих 2 команд в MacOS:

find . -name "*.txt" -exec sed -i '' '1 s/([^()]*)//g' {} + 
find . -name '*.txt' -print0 | xargs -0 sed -i '' '1 s/([^()]*)//g'

приводит к тому же результату. Только «первый» найденный файл выполняет операцию sed. Похоже, это из-за 1 в sed -i '' '1 s/([^()]*)//g'. Странно то, что, хотя это приводит к использованию только первого файла, он также выполняет только замену sed в первой строке этого файла; что и должно быть.

Если я изменю команду на эту

find . -name '*.txt' -print0 | xargs -0 sed -i '' '2 s/([^()]*)//g'

, то это все еще только первый файл, который изменен, но теперь вторая строка имеет замену. Мой вопрос, поэтому, почему это только влияет на первый файл, возвращенный

find . -name '*.txt' -print0

Редактировать для уточнения

Я должен уточнить, что именно я имею в виду под только в «первом» файле выполняется операция sed, воссоздающая проблему шаг за шагом. Во-первых,

Это иерархия папок (обратите внимание на пробел в «папке 1»):

.
├── folder\ 1
│   └── test1.txt
├── folder2
│   └── test2.txt
├── folder3
│   └── test3.txt
└── folder4
    └── test4.txt

Каждый файл .txt содержит именно эту, и только эту, одну строку:

This should stay (this should go)

При выполнении любой из вышеприведенных команд изменяется файл test2.txt, и проблема в том, что это единственный файл, который изменяется!

Итак, теперь файлы содержат следующее:

test1.txt : This should stay (this should go)

test2.txt : This should stay

test3.txt : This should stay (this should go)

test4.txt : This should stay (this should go)

Я считаю, что это потому, что Первая часть команды, например

find . -name '*.txt' -print0

, дает следующее (каждая разделяется \0 нулевым символом)

./folder2/test2.txt./folder3/test3.txt./folder4/test4.txt./folder 1/test1.txt

Путем случайного изменения имен папок и файлов, ясно, что это всегда первый файл в приведенном выше списке \0, который изменяется.

Таким образом, остается вопрос, что это за вызов sed, который предотвращает его вызов на AL L файлов?

Спасибо!

1 Ответ

2 голосов
/ 15 января 2020

Полагаю, что на вопрос о 1-й команде ответила Бета, и позвольте мне ответить на 2-й.

Попробуйте установить -t (test) в xargs и посмотреть, как работает командная строка расширен:

find . -name '*.txt' -print0 | xargs -0 -t sed -i '' '1 s/([^()]*)//g'

Будет выведено что-то вроде:

sed -i '' 1 s/([^()]*)//g ./test1.txt ./test2.txt ./test3.txt ./test4.txt

Поведение по умолчанию xargs - выполнение указанной команды (в данном случае sed) сразу с все аргументы считываются из стандартного ввода.
Кроме того, sed не сбрасывает нумерацию строк в нескольких входных файлах, и приведенная выше команда s будет применяться только к 1-му файлу.

You можно изменить поведение xargs с помощью опции -l1:

find . -name '*.txt' -print0 | xargs -0 -l1 -t sed '1 s/([^()]*)//g'

Вывод:

sed -i '' 1 s/([^()]*)//g ./test1.txt
sed -i '' 1 s/([^()]*)//g ./test2.txt
sed -i '' 1 s/([^()]*)//g ./test3.txt
sed -i '' 1 s/([^()]*)//g ./test4.txt

Тогда sed будет работать как положено.

...