Отключить экранирование одинарных кавычек внутри строки из встроенной команды чтения 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
Другие вопросы по тегам