#bash #printf
#bash #printf
Вопрос:
Bash может выводить переменные в виде строк, экранированных оболочкой:
$ X='1 2 te$t'; Y=$' ant '; printf '%q %qn' "$X" "$Y" > .data
$ cat .data
1 2 te$t $' ant '
Возможно ли восстановить переменные X и Y из file .data? Это должно быть безопасно, даже если .data содержит $(rm -rf /*)
.
Я ищу способ хранения записей (переменных X, Y) в формате «одна строка на запись».
Редактировать
В моей задаче printf '%q'
может быть заменен urlquote из Python. Решение:
$ X='1 2 te$t'; Y=$' ant ';
$ alias urlquote="python -c'import urllib, sys; print urllib.quote(sys.stdin.read())'"
$ alias urlunquote="python -c'import urllib, sys; print urllib.unquote(sys.stdin.read())'"
$ echo $(echo -n "$X" | urlquote) $(echo -n "$Y" | urlquote) > .data
$ cat .data
1 2 te$t
$ while read -r x y; do
> x=$(echo "$x" | urlunquote)
> y=$(echo "$y" | urlunquote)
> printf '%q %qn' "$x" "$y"
> done < .data
1 2 te$t $' ant '
Ответ №1:
Обновленный ответ:
вывод printf % q, насколько я знаю, не может быть восстановлен без eval, и eval всегда будет уязвим для выполнения произвольной команды. Вам нужно будет использовать кодировку, напрямую не поддерживаемую bash, например, с base64:
#!/bin/bash
X='1 2 te$t';
Y=$' ant ';
echo "$(base64 -w0<<<"$X") $(base64 -w0<<<"$Y")" > .data
cat .data|while read line; do
X2=$(base64 -d<<<${line%% *})
Y2=$(base64 -d<<<${line#* })
printf "%q %qn" "$X2" "$Y2"
done
(исходный ответ удален, смотрите историю сообщений)
Комментарии:
1. Это опасно для таких файлов, как
boom $(date)
.2. @sehe — это не опечатка, $(.. что такое $<(.data)?
3. @je4d: ах да, моя путаница была безумной, я никогда раньше не видел «неявного кота» с помощью «перенаправления голого ввода». 1
4. @dmage — прошу прощения, вы совершенно правы. AFAIK printf %q — это единственный способ идеально сохранить / восстановить переменную с использованием чистого bash, и у вас нет другого выбора, кроме как использовать eval для ее восстановления. За исключением некоторых старых версий, где decl массива работали без оценки из-за ошибки, и они могут быть уязвимы в любом случае. Вам придется прибегнуть к использованию двух строк в кодировке base64 на строку в вашем файле и использовать внешнее приложение для преобразования
5. Надежный подход, но стоит отметить, что
base64
это не стандартная утилита и что-w
опция зависит от GNU (BSDbase64
никогда не переносит строки), а BSDbase64
использует-D
вместо (-d
) для декодирования. Кроме того, я предлагаю не использоватьwhile
цикл в подоболочке , потому что целью чтения из файла является создание переменных для последующего использования. Используйтеwhile … done < .data
вместо этого.
Ответ №2:
Я уверен, что поступил бы так
X='1 2 te$t'
Y=$' ant '
{
printf "X=%q" "$X"
printf "Y=%q" "$Y"
} > .data
Затем для восстановления:
source .data
Редактировать
Почему бы вам просто
(echo "$X"; echo "$Y") > .data
readarray ENVVARS < .data
X="${ENVVARS[0]}"
Y="${ENVVARS[1]}"
Обратите внимание, что readarray
требуется bash 4 . Вы могли бы приблизительно сделать то же самое с read
Комментарии:
1. Да, но что, если кто-нибудь заменит .data на
rm -rf /
? Я ищу простое хранилище, удобное для работы с bash. Это никогда не должно разрушить мою систему.2. @dmage: предложил более безопасный подход
3. @dmage: Я это знаю. К чему вы клоните? Если вам все это требуется, почему бы вам не упомянуть об этом в своем сообщении? Мы теряем время здесь
4. Y= $’ ant ‘… Мне нужны все символы для имен файлов (все, кроме ). Я хочу сохранить пары <имя файла> <комментарий> в текстовом файле (т. Е. не должен быть разделителем). Возможно, bash использует неправильный язык для этой задачи…
5. @dmage: мы были бы признательны, если бы вы могли указать такие цели / требования в вопросе в следующий раз
Теперь, поскольку вам требуются все символы для имен файлов, вы застряли: во-первых,
ДОЛЖЕН быть разделитель (ничто другое не является корректным по умолчанию), а во-вторых, вы не можете иметь,
records (variables X, Y) in format "one line per record."
потому что именам файлов разрешено столько символов новой строки, сколько они считают правильным. К сожалению, я не знаю способов заставить readarray принимать «строки» с разделителями 0, поэтому, возможно, вам понадобится настоящий язык программирования (perl, python, ruby, c и т. Д.)