filename... dangerous to eval
Конечно.
$ touch 'file $(date >&2).txt'
$ bash -c 'eval ls *'
Wed 03 Feb 2021 06:07:08 PM EET
ls: cannot access 'file': No such file or directory
ls: cannot access '.txt': No such file or directory
Не eval
вещи.
or backtick the output of ls in a bash script?
Я не совсем понимаю, что это значит.
Если вы имеете в виду то, что у вас было в качестве примера,
hello=(`ls -t`)
или с более разумным синтаксисом,
files=( $(ls -t) )
, то вы просто получаете вывод ls
wordsplit в массив.
$ declare -p files
declare -a files=([0]="file" [1]="\$(date" [2]=">&2).txt")
Самая большая проблема здесь, еще до возможной инъекции команды, заключается в том, что пробел в имени файла сломал его , мы получили две записи массива вместо одной. См. страницу о разборе ls на вики Грега. Нет, вы не можете обойти это, добавляя кавычки, разделение слов так не работает.
Поэтому не используйте здесь ls
. Просто позвольте оболочке сгенерировать список имен файлов:
files=(*)
declare -p files
declare -a files=([0]="file \$(date >&2).txt")
Единственная проблема заключается в том, что Bash не дает хорошего способа сортировки файлов по дате, поэтомуls -t
выглядит заманчиво.Хорошей альтернативой является указание даты в самом имени файла , чтобы сортировка по умолчанию давала вам сортировку по дате , или использование Zsh, так как он может сортировать по дате . Или сделайте уродливые хаки для решения проблемы (предостережение, я не тестировал это решение ).
Та же проблема с eval
возникает, если вам нужно передать имя файла чему-либо, что принимает только сценарий оболочки, например
ssh somehost "do something with $file" # wrong
ssh somehost "do something with '$file'" # still wrong, the name
# could contain single ticks
It seems like the filesystem sanitizes special characters, spaces for instance, by wrapping the filenames in quotes. Will it catch all such things?
О боже, о нет, это не так. Если бы файловая система сделала что-то, чтобы предотвратить сохранение специальных символов в оболочке, половина сообщений в unix.SE не понадобилась бы.
Если вы хотите испытать боль от слишком большого количества знаний, вот эссе Дэвида Уилера об этом:Исправление имен файлов Unix/Linux/POSIX :Управляющие символы (, такие как перевод строки ), начальные тире и другие проблемы
Есть еще один, написанный им, Имена файлов и пути в оболочке :Как это сделать правильно . Многое из этого также обсуждалось здесь, на unix.SE.
Кроме того, кавычки не помогают.
$ touch '"quoted name"' 'othername' # two files
$ printf "%s\n" $(ls) # oops
othername
"quoted
name"
$ printf "%s\n" * # this works better
othername
"quoted name"
Потому что, когда происходит разделение слов, кавычки — это просто обычный символ. (Если вы не установите IFS
для включения кавычек, что, вероятно, только усугубит ситуацию. )Кроме того, даже если имя заключено в кавычки, оно все равно может иметь кавычки посередине , нарушая это правило. Вам также нужно будет позаботиться о побеге или правильно процитировать их.
Цитаты делает GNU ls, в зависимости от версии и настроек:
$ ls -l
total 0
-rw-rw-r-- 1 ilkkachu ilkkachu 0 Feb 3 18:30 othername
-rw-rw-r-- 1 ilkkachu ilkkachu 0 Feb 3 18:30 '"quoted name"'
То же, что и ls --quoting-style=shell
. Между прочим, кажется, что это правильно для всех новых строк, знаков доллара и кавычек. Но доверяете ли вы ему, чтобы сделать это правильно? Если вы это сделаете, и вы знаете, как правильно их использовать, вы сможете использовать их для получения отсортированного списка.
hello=(`ls -t`)
Эта форма кажется безопасной. Bash выполнит просто split + glob для результатов подстановки команд, а не интерпретирует это как синтаксис :
.a=(`echo '[0]=1'`)
typeset -p a
declare -a a=([0]="[0]=1") # aha!
Тем не менее, (непонятные)declare "var=val"
или local "var=val"
формы не являются таковыми, поскольку они работают аналогичноeval "var=val"
:
cd "$(mktemp -d)"
touch '$(yes BOOBS >&2&)'
declare -a "a=($(ls))"
BOOBS
BOOBS
BOOBS
...
То же, что и declare -a a="($(ls))"
, declare -a a="(`ls`)"
и т. д.
Или даже с не -цитируемыми формами, при условии, что переменная уже была объявлена как массив:
cd "$(mktemp -d)"
a=(1 2 3)
touch '($(yes BOOBS >&2&))'
declare a=$(ls) # I forgot that a was an array
BOOBS
BOOBS
BOOBS
...