удалять конечные пробелы из текстовых файлов ТОЛЬКО при необходимости

удалять конечные пробелы из текстовых файлов ТОЛЬКО при необходимости

Я удаляю конечные пробелы с помощью

sed -i 's/[ \t]*$//' *.txt

Однако эта команда перезапишет все файлы.

Существует ли удобный способ определить, есть ли в текстовом файле конечные пробелы, и пропустить те, в которых их нет?

решение1

Вы можете использовать grepfirst, чтобы найти строки, которые необходимо изменить, хотя в худшем случае это все равно приведет к считыванию файлов дважды (в случае, если требуется изменить только последнюю строку):

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

Связанный контент