Как прочитать файл в переменную в оболочке?

262

Я хочу прочитать файл и сохранить его в переменной, но мне нужно сохранить переменную, а не просто распечатать файл. Как я могу это сделать? Я написал этот script, но это не совсем то, что мне нужно:

#!/bin/sh
while read LINE  
do  
  echo $LINE  
done <$1  
echo 11111-----------  
echo $LINE  

В моем script я могу указать имя файла в качестве параметра, поэтому, если файл содержит "aaaa", например, он распечатает это:

aaaa
11111-----

Но это просто печатает файл на экране, и я хочу сохранить его в переменной! Есть ли простой способ сделать это?

Теги:
sh

3 ответа

606
Лучший ответ

В кросс-платформенном, самом низком-общем знаменателе sh вы используете:

#!/bin/sh
value=`cat config.txt`
echo "$value"

В bash или zsh, чтобы прочитать целый файл в переменной без вызова cat:

#!/bin/bash
value=$(<config.txt)
echo "$value"

Вызов cat в bash или zsh для удаления файла будет считаться Бесполезное использование Cat.

Обратите внимание, что нет необходимости процитировать замену команд для сохранения новых строк.

Смотрите: Bash Hacker Wiki - Замена команд - Специальности.

  • 4
    Это правильный ответ и должен быть помечен как таковой.
  • 4
    Хорошо, но это bash, а не sh; это может не соответствовать всем случаям.
Показать ещё 14 комментариев
56

Если вы хотите прочитать весь файл в переменной:

#!/bin/bash
value=`cat sources.xml`
echo $value

Если вы хотите прочитать его по очереди:

while read line; do    
    echo $line    
done < file.txt
  • 1
    как насчет просто чтения первой строки или строки (n)
  • 2
    @brain: Что делать, если файл Config.cpp и содержит обратную косую черту; двойные кавычки и кавычки?
Показать ещё 2 комментария
48

Две важные ошибки

которые до сих пор игнорировались другими ответами:

  • Удаление новой строки из расширения команды
  • Удаление символа NUL

Отключение новой строки от расширения команды

Это проблема для:

value="$(cat config.txt)"

но не для решений на основе read.

Расширение команды удаляет завершающие символы новой строки:

S="$(printf "a\n")"
printf "$S" | od -tx1

Выходы:

0000000 61
0000001

Это нарушает наивный метод чтения из файлов:

FILE="$(mktemp)"
printf "a\n\n" > "$FILE"
S="$(<"$FILE")"
printf "$S" | od -tx1
rm "$FILE"

Обходное решение POSIX: добавьте дополнительный char к расширению команды и удалите его позже:

S="$(cat $FILE; printf a)"
S="${S%a}"
printf "$S" | od -tx1

Выходы:

0000000 61 0a 0a
0000003

Почти обходное решение POSIX: кодировка ASCII. См. Ниже.

Удаление символа NUL

Нет смысла использовать Bash способ хранения символов NUL в переменных.

Это влияет как на решения расширения, так и на read, и я не знаю никаких хороших путей для этого.

Пример:

printf "a\0b" | od -tx1
S="$(printf "a\0b")"
printf "$S" | od -tx1

Выходы:

0000000 61 00 62
0000003

0000000 61 62
0000002

Ха, наш NUL ушел!

Обходные:

  • Кодирование ASCII. См. Ниже.

  • использовать Bash extension $"" литералы:

    S=$"a\0b"
    printf "$S" | od -tx1
    

    Работает только для литералов, поэтому не полезно читать из файлов.

Обходной путь для подводных камней

Сохраните кодированную версию файла uuencode base64 в переменной и декодируйте перед каждым использованием:

FILE="$(mktemp)"
printf "a\0\n" > "$FILE"
S="$(uuencode -m "$FILE" /dev/stdout)"
uudecode -o /dev/stdout <(printf "$S") | od -tx1
rm "$FILE"

Вывод:

0000000 61 00 0a
0000003

uuencode и udecode POSIX 7, но не в Ubuntu 12.04 по умолчанию (sharutils package)... Я не вижу Альтернатива POSIX 7 для процесса расширения Bash process <(), кроме записи в другой файл...

Конечно, это медленно и неудобно, поэтому я предполагаю, что реальный ответ: не используйте Bash, если входной файл может содержать символы NUL.

  • 2
    Спасибо, только это сработало для меня, потому что мне нужны были новые строки
  • 1
    @CiroSantilli: что делать, если FILE является Config.cpp и содержит обратную косую черту; двойные кавычки и кавычки?
Показать ещё 6 комментариев

Ещё вопросы

Сообщество Overcoder
Наверх
Меню