Как мне прочитать файл в переменную с неизвестным количеством кавычек в Bash

#bash #git-bash

#bash #git-bash

Вопрос:

Я пытаюсь прочитать файл в переменную с git-bash помощью . Я использую что-то вроде этого:

 readFile() {
    local file="${1}"
    local resultVar="${2}"
    eval $resultVar="'$(cat ${file})'"
}
 

Аналогично тому, что предлагается здесь по ссылке.

В большинстве случаев это работает нормально. Но это может вызвать проблемы в зависимости от количества кавычек в файлах. Например:

Пример 1 ❌

test1.txt :

 text "quoted"

 

code :

 $ echo "text "quoted"" > test1.txt amp;amp;
> readFile "./test1.txt" test1 amp;amp;
> printf "test1: %sn" "${test1}"
test1: text "quoted"
 

ОШИБКА: вырезание конца n в конце.

Пример 2 ❌

test2.txt :

 text "one quote

 

code :

 $ echo "text "one quote" > test2.txt amp;amp;
> readFile "./test2.txt" test2 amp;amp;
> printf "test2: %sn" "${test2}"
test2: text "one quote
 

ОШИБКА: вырезание конца n в конце.

Пример 3 ❌

test3.txt :

 text 'quoted'

 

code :

 $ echo "text 'quoted'" > test3.txt amp;amp;
> readFile "./test3.txt" test3 amp;amp;
> printf "test3: %sn" "${test3}"
test3: text quoted
 

ОШИБКА: они single-quotes были удалены!

Пример 4 ❌

test4.txt :

 text 'one quote

 

code :

 $ echo "text 'one quote" > test4.txt amp;amp;
> readFile "./test4.txt" test4 amp;amp;
> printf "test4: %sn" "${test4}"
bash: unexpected EOF while looking for matching `''
bash: syntax error: unexpected end of file
 

ОШИБКА: это становится еще хуже…

Пример 5 ❌

test5.txt :

 text 'quoted"

 

code :

 $ echo "text 'quoted"" > test5.txt amp;amp;
> readFile "./test5.txt" test5 amp;amp;
> printf "test5: %sn" "${test5}"
bash: unexpected EOF while looking for matching `"'
bash: syntax error: unexpected end of file
 

ОШИБКА: аналогично приведенному выше.


Итак, как я могу надежно прочитать файл из функции в переменную, не зная, содержит ли он кавычки, сколько и какого типа?

Возможно, есть другие символы, которые также могут нарушить мой код, но я этого не проверял. Было бы неплохо, если бы решение также позаботилось об этом.

Ответ №1:

Может ли это достичь того, чего вы хотели?

 #!/usr/bin/env bash
  
readFile() {
    IFS= read -rd '' "$1" < "$2"
}   

readFile var data-file

# Checking result
printf %s "$var" | xxd
 

Комментарии:

1. Это действительно так! readarray Версия KamilCuk также работает, но я принял эту, потому что она немного быстрее. Спасибо!

Ответ №2:

Не используйте eval.

В bash , вы можете $(<file) вместо $(cat file) . Это просто немного быстрее.

Вы можете использовать namereference:

 readFile() {
    declare -n resultVar=$2
    resultVar="$(<"$1")"
}
 

Если нет нулевых байтов, вы можете использовать readarray / mapfile . Примечание — он сохранит конечную новую строку, в отличие $(...) от того, что удаляет конечные новые строки:

 readFile() {
    readarray -d '' -t "$2" < "$1"
}
 

Если вы действительно хотите использовать eval , то используйте declare :

 readFile() {
    declare -g "$2=$(< "$1")"
}
 

Если вы действительно действительно хотите использовать eval , всегда передавайте ему правильно экранированную строку, т.е.. всегда после printf "%q" :

 readFile() {
    eval "$(printf "%q" "$2")=$(printf "%q" "$(< "$1")")"
}
 

Комментарии:

1. Гораздо лучшее решение, чем eval есть printf -v "$2" "%q" "$(< "$1")" или, возможно, только printf -v "$2" "%s" "$1" если это то, что вы хотите.

2. @tripleee Я думаю, вы имели в виду printf -v "$2" "%s" "$(<"$1")" в своем 2-м примере. Но это также отсекает n в конце файла. n s перед этим обрабатываются правильно. Можно ли это изменить, чтобы оно работало? (Я скорректировал вопрос, поскольку узнал, что мои первые 2 примера также вырезали конечную n строку)

3. Если вы хотите сохранить файл как есть, я бы предложил кодировать содержимое файла в шестнадцатеричном формате с помощью ex . xxd . $(...) удаляет завершающие символы новой строки, а bash не способен хранить нулевые байты. Т.Е.. bash не подходит для обработки двоичных данных, это оболочка, которую лучше всего использовать для обработки удобочитаемых строк, которые выглядят красиво.

4. Подстановки команд по определению удаляют все завершающие новые строки из захваченного значения. Это то, что вы делаете?

5. Не делайте этого. Прочитать файл в поток — в потоке изменить содержимое ( sed awk большинство инструментов unix работают с потоками , а не с состоянием) — сохранить в файл. Один из наиболее распространенных инструментов unix — sed буквально назван в честь редактора потоков. Как вы хотите изменить содержимое файла? Потоки — самый естественный способ работы в оболочках. Вы добавляете дополнительный шаг «переменная» — он не нужен. Измените файл, а не переменную.