Скрипт ниже в настоящее время удаляет символ ^M ( Ctrl+V+M
). Я чувствую, что он немного длинноват, но мне также нужно добавить ^I и любые другие символы, которые я могу увидеть в будущем.
Есть ли более простой способ добавить ^I ( Ctrl+V+I
)? Это первый скрипт, который я написал для себя около 6 месяцев назад после посещения 2-дневного курса программирования оболочки. Я не уверен, сделал ли я его длиннее, чем нужно, поэтому любые общие советы также будут оценены.
#!/bin/bash
echo "$# item(s) to review."
question='Do you want to remove the ^M characters?'
for file
do
if grep "^M" "$file" >> /dev/null 2> /dev/null
then
echo "$file contains special characters"
echo $question
read answer
if [[ "$answer" == [yY] ]]
then
cat "$file" | sed "s/^M//" > "$file.safe"
echo "Special characters have been removed and $file.safe has been created."
elif [[ "$answer" == [yY][eE][sSaA]* ]]
then
cat "$file" | sed "s/^M//" > "$file.safe"
echo "Special characters have been removed and $file.safe has been created."
else
echo "Special characters have NOT been removed."
fi
elif [[ -d $file ]]
then
echo "$file is a directory"
else
echo "No special characters in $file"
fi
done
решение1
Это, конечно, намного, намного дольше, чем нужно. Все, что вам нужно, этоtr
полезность, а также цикл и перенаправления для обработки файлов, которые передаются в качестве аргументов скрипту.
#!/bin/sh
for file do
tr -d '\r\t' <"$file" >"$file.safe"
done
С опцией -d
удаляет tr
указанные символы. Символы для удаления передаются вместе как первый аргумент без опции. Вы можете использовать экранированные обратные косые черты для представления специальных символов: \n
для новой строки (^J), \r
для возврата каретки (^M), \t
для табуляции (^I) и т. д.
Я не воспроизвел код для запроса пользователя, потому что это бессмысленно. Каталоги в любом случае вызовут ошибку при перенаправлении, и на самом деле это работа вызывающего, чтобы не запрашивать бессмысленное действие, например, обработку каталога как обычного файла, поэтому я также пропустил эту часть.
Если вы хотите заменить исходный файл, запишите его во временный файл, а затем переместите результат на то же место.
#!/bin/sh
for file do
tmp="$(TMPDIR=$(dirname -- "$file") mktemp)"
tr -d '\r\t' <"$file" >"$tmp" && mv -f -- "$tmp" "$file"
done
Имя временного файла создается с использованием , mktemp
чтобы скрипт был надежным. Он будет работать, пока у вас есть разрешение на запись в каталог, содержащий файл, без риска перезаписи существующего файла. Он безопасен, даже если этот каталог доступен для записи другим пользователям, которые могут попытаться внедрить другие данные (потенциальная проблема в /tmp
).
Команда mv
вызывается только в том случае, если вызов был tr
успешным, поэтому нет риска потери данных в случае tr
сбоя, например, из-за того, что диск заполнится в середине выполнения.
Если вы хотите избежать замены файла новым идентичным файлом, если он не содержит никаких специальных символов, есть два способа:
Сначала вы можете проверить наличие специальных символов. Есть несколько способов сделать это. Один из способов — удалить все, кроме этих специальных символов, и подсчитать количество полученных символов. В качестве оптимизации используйте конвейер,
head -c 1
чтобы вам не пришлось проходить по всему файлу, если специальный символ будет найден близко к началу: таким образом, счетчик будет равен 0, если ничего не нужно делать, и 1 в противном случае.if [ "$(tr -dc '\r\t' <"$file" | head -c 1 | wc -c)" -ne 0 ]; then tr -d '\r\t' <"$file" >"$tmp" && mv -f -- "$tmp" "$file" fi
Вы можете выполнить преобразование, а затем проверить, идентично ли оно оригиналу. Это может быть медленнее, если файлы часто уже находятся в желаемом состоянии. С другой стороны, этот метод обобщается на случаи, когда нелегко определить, находится ли файл в желаемом состоянии.
tr -d '\r\t' <"$file" >"$tmp" && if cmp -s "$tmp" "$file"; then rm -- "$tmp" else mv -f -- "$tmp" "$file" fi
решение2
Вы можете зациклить свой сценарий. Итак:
for c in "^I" "^M"; do
for file; do
if grep "$c" "$file"; then
...
etc.
...
fi
done
done
решение3
Я предпочитаю этот perl one liner. '\cM' — это символ control-M. Исходный файл(ы) будет сохранен с расширением '.bak' Это расширение может быть выбрано вами.
perl -i.bak -pe 's/\cM//g;' file(s)
Пример использования класса символов для удаления. В скобках perl найдет control-I и control-M и удалит их. Я не проверял это точно.
perl -i.bak -pe 's/[\cM\cI]//g;' files(s)
решение4
Вы думали об использовании
tr -d .....<characterlist>....
Например, удалите все непечатаемые символы и поместите их в другой файл:
cat filename | tr -cd '[:print:]' >/tmp/x.out
Измените список символов в соответствии с вашим приложением... tr
для получения дополнительной информации см. страницу руководства.
Также это удобно, поскольку разрешены диапазоны регулярных выражений:
echo '\001\002\003\004' | tr -d '[\001-\003]' | od -c