Используя bash, как передать аргументы имени файла команде, отсортированной по дате и имеющей дело с пробелами и другими специальными символами? - PullRequest
1 голос
/ 29 октября 2019

Я использую оболочку bash и хочу выполнить команду, которая принимает имена файлов в качестве аргументов;скажи команду кота. Мне нужно предоставить аргументы, отсортированные по времени модификации (сначала самое старое), и, к сожалению, имена файлов могут содержать пробелы и несколько других сложных символов, таких как "-", "[", "]". Все файлы, которые должны быть предоставлены в качестве аргументов, являются файлами * .txt в моем каталоге. Я не могу найти правильный синтаксис. Вот мои усилия.

Конечно, cat *.txt терпит неудачу;он не дает желаемый порядок аргументов.

cat `ls -rt *.txt`

`ls -rt *.txt` дает желаемый порядок, но теперь пробелы в именах файлов вызывают путаницу;команда cat видит их как разделители имен файлов.

cat `ls -brt *.txt`

Я пытался -b экранировать неграфические символы, но пробелы по-прежнему рассматриваются как разделители имен файлов.

cat `ls -Qrt *.txt`

Я пытался -Q поставить имена записей в двойных кавычках.

cat `ls -rt --quoting-style=escape *.txt`

Я пробовал этот и другие варианты стиля цитирования.

Ничего из того, что я пробовал, не работает. Либо пропуски обрабатываются cat как разделители имен файлов, либо весь список имен файлов рассматривается как один (недопустимый) аргумент. Пожалуйста, сообщите!

Ответы [ 2 ]

1 голос
/ 30 октября 2019

Использование --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[@]}"

Вот так. Это не красиво. Это не элегантно. Но это безопасно.

0 голосов
/ 30 октября 2019

Делает ли это то, что вы хотите?

for i in $(ls -rt *.txt); do echo "FILE: $i"; cat "$i"; done
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...