Я удаляю конечные пробелы с помощью
sed -i 's/[ \t]*$//' *.txt
Однако эта команда перезапишет все файлы.
Существует ли удобный способ определить, есть ли в текстовом файле конечные пробелы, и пропустить те, в которых их нет?
решение1
Вы можете использовать grep
first, чтобы найти строки, которые необходимо изменить, хотя в худшем случае это все равно приведет к считыванию файлов дважды (в случае, если требуется изменить только последнюю строку):
for f in ./*.txt; do
grep -q '[[:blank:]]$' "$f" &&
sed -i 's/[[:blank:]]*$//' "$f"
done
решение2
Один из способов может быть таким, find/grep/sed
как показано:
find . -maxdepth 1 -type f -name '*.txt' \
-exec grep -q '[[:blank:]]$' {} \; \
-exec sed -Ei -e 's/[[:blank:]]+$//' {} +
- поиск с -maxdepth, если 1, работает в текущем каталоге.
{} \;
ссылается на имя файла, которому передается,grep
и\;
экранирует метасимвол оболочки точка с запятой, которая является индикатором конца команды. Мы экранируем его, чтобы он достиг-exec
. Вы могли бы также записать это как';'
{} +
вы уже знаете{}
это сейчас, и это+
означает, что вы передаете столько имен файлов, сколько возможноsed
(в основном, вместо передачи единственного{}
текущегоfind
результата, накапливаете список для{}
использования в качестве аргумента перед вызовомsed
). Это позволяет нам минимизировать количествоsed
вызовов.
решение3
Выполните редактирование и замените исходный файл только в случае обнаружения различий.
for file in *.txt
do
sed 's/[ \t]*$//' < "$file" > "$file.tmp.$$" || continue
cmp -s -- "$file" "$file.tmp.$$" ||
cat < "$file.tmp.$$" > "$file" ||
continue
rm -f -- "$file.tmp.$$"
done
решение4
Следующее основывается наидея Роаймывыполнить sed
выражение по всем файлам, но затем сохранить только те файлы, которые были фактически изменены.
Это изменяет ситуацию, делая меньше sed
вызовов:
printf '%s\0' ./*.txt |
xargs -0 sed -i.bak 's/[[:blank:]]$//
Затем вы можете просмотреть *.txt
файлы, сравнить их с оригиналами и выбрать тот, который вы хотите сохранить:
for name in ./*.txt; do
if cmp -s "$name" "$name.bak"; then
# keep original
mv "$name.bak" "$name"
else
# keep modified
rm "$name.bak"
fi
done
Или, сделав оба действия одновременно:
printf '%s\0' ./*.txt |
xargs -0 sh -c '
sed -i.bak "s/[[:blank:]]$//" "$@"
for name do
if cmp -s "$name" "$name.bak"; then
mv "$name.bak" "$name"
else
rm "$name.bak"
fi
done' sh