Отключить экранирование одинарных кавычек внутри строки из встроенной команды чтения bash
Я хочу обрабатывать файлы из текстового файла, содержащего одинарные кавычки, например
'new'$'\n''line'
'tab'$'\t''ulator'
Копировать и вставить для ручной обработки этих файлов отлично работает:
test -f 'tab'$'\t''ulator'
теперь, чтение из файла с помощью встроенной команды чтения bash
while IFS="" read -r myfile; do
line=$myfile
...
done < text.txt
давать строки, содержащие экранированные одинарные кавычки, например
'\''new'\''$'\''\n'\'''\''line'\'''
'\''tab'\''$'\''\t'\'''\''ulator'\'''
однако обработка этих имен файлов в скрипте bash не работает.
test -f "$myfile"
test -f ${myfile}
как отключить / отменить экранирование одинарных кавычек и обработать необработанное имя файла в bash?
2 ответа
С помощью eval
Многие люди вполне обоснованно расценивают eval
как неправильное написание зла. Таким образом, я бы расценил это решение как последний вариант, который будет использоваться, только если все остальное терпит неудачу.
Давайте возьмем этот пример файла:
$ cat badformat
'new'$'\n''line'
'tab'$'\t''ulator'
Мы можем прочитать и интерпретировать эти имена файлов, как в следующем примере:
while read -r f; do
eval "f=$f"; [ -f "$f" ] || echo "file not found"
done <badformat
Использование разделенных NUL списков имен файлов
Единственный символ, который не может быть в имени файла Unix, - это NUL (hex 00). Следовательно, многие инструменты Unix предназначены для работы со списками, разделенными NUL.
Таким образом, при создании файла замените:
stat -c %N * >badformat
с:
printf '%s\0' * >safeformat
Этот последний файл может быть прочитан в сценарии оболочки через цикл while-read. Например:
while IFS= read -r -d $'\0' f; do
[ -f "$f" ] || echo "file not found"
done <safeformat
В дополнение к циклам while-read оболочки, обратите внимание, что grep
, find
, sort
, xargs
, а также GNU sed
и GNU awk
все имеют встроенную способность обрабатывать NUL-разделенные списки. Таким образом, подход с разделением NUL-списков является как безопасным, так и хорошо поддерживаемым.
Нашел решение со строковыми манипуляциями
${filename//$'\047'\\$'\047'$'\047'/$'\047'}
как вы упоминали выше, использование eval очень опасно для имен файлов, таких как 'rm -rf'. Что касается stat -c %N (который выходит только из одинарных кавычек, перевода строки и табуляции), есть другое решение
while IFS="" read -r myfile; do
filename="$myfile"
filename="${filename#?}"
filename="${filename%?}"
filename="${filename//"'$'\t''"/$'\011'}"
filename="${filename//"'$'\n''"/$'\012'}"
filename="${filename//$'\047'\\$'\047'$'\047'/$'\047'}"
test -f "$filename" && echo "$myfile exists"
done < text.txt