Использование --quoting-style
- хорошее начало. Хитрость в том, чтобы разобрать имена файлов в кавычках. Обратные палки просто не до работы. Нам нужно быть очень явным при разборе escape-последовательностей.
Во-первых, нам нужно выбрать стиль цитирования. Давайте посмотрим, как различные алгоритмы обрабатывают сумасшедшее имя файла, например "foo 'bar'\tbaz\nquux"
. Это имя файла, содержащее реальные одинарные и двойные кавычки, а также пробел, символ табуляции и символ новой строки для загрузки. Если вам интересно: да, все это законно, хотя и необычно.
$ for style in literal shell shell-always shell-escape shell-escape-always c c-maybe escape locale clocale; do printf '%-20s <%s>\n' "$style" "$(ls --quoting-style="$style" '"foo '\''bar'\'''$'\t''baz '$'\n''quux"')"; done
literal <"foo 'bar' baz
quux">
shell <'"foo '\''bar'\'' baz
quux"'>
shell-always <'"foo '\''bar'\'' baz
quux"'>
shell-escape <'"foo '\''bar'\'''$'\t''baz '$'\n''quux"'>
shell-escape-always <'"foo '\''bar'\'''$'\t''baz '$'\n''quux"'>
c <"\"foo 'bar'\tbaz \nquux\"">
c-maybe <"\"foo 'bar'\tbaz \nquux\"">
escape <"foo\ 'bar'\tbaz\ \nquux">
locale <‘"foo 'bar'\tbaz \nquux"’>
clocale <‘"foo 'bar'\tbaz \nquux"’>
Те, которые на самом деле занимают две строки, не годятся, поэтому literal
, shell
и shell-always
вне. Умные кавычки не помогают, поэтому locale
и clocale
отсутствуют. Вот что осталось:
shell-escape <'"foo '\''bar'\'''$'\t''baz '$'\n''quux"'>
shell-escape-always <'"foo '\''bar'\'''$'\t''baz '$'\n''quux"'>
c <"\"foo 'bar'\tbaz \nquux\"">
c-maybe <"\"foo 'bar'\tbaz \nquux\"">
escape <"foo\ 'bar'\tbaz\ \nquux">
С какими из них мы можем работать? Ну, мы в сценарии оболочки. Давайте использовать shell-escape
.
В каждой строке будет одно имя файла. Мы можем использовать цикл while read
для чтения строки за раз. Нам также понадобятся IFS=
и -r
, чтобы отключить обработку любых специальных символов. стандартный цикл обработки строк выглядит следующим образом:
while IFS= read -r line; do ... done < file
Этот "файл" в конце должен быть именем файла, но мы не хотим читать из файламы хотим прочитать из команды ls
. Давайте использовать <(...)
процесс подстановки для замены команды, в которой ожидается имя файла.
while IFS= read -r line; do
# process each line
done < <(ls -rt --quoting-style=shell-escape *.txt)
Теперь нам нужно преобразовать каждую строку со всеми символами в кавычках в пригодный для использования файлимя. Мы можем использовать eval
, чтобы оболочка интерпретировала все escape-последовательности. (Я почти всегда предостерегаю от использования eval
, но это редкая ситуация, когда все в порядке.)
while IFS= read -r line; do
eval "file=$line"
done < <(ls -rt --quoting-style=shell-escape *.txt)
Если вы хотите работать с одним файлом за раз, мы будемсделанный. Но вы хотите передать все имена файлов одновременно другой команде. Чтобы добраться до финиша, последним шагом является создание массива со всеми именами файлов.
files=()
while IFS= read -r line; do
eval "files+=($line)"
done < <(ls -rt --quoting-style=shell-escape *.txt)
cat "${files[@]}"
Вот так. Это не красиво. Это не элегантно. Но это безопасно.