Результат чтения printf ‘%q’ «$VAR»

#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 (BSD base64 никогда не переносит строки), а BSD base64 использует -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 и т. Д.)