Как прочитать файл в переменную в оболочке? - PullRequest
398 голосов
/ 15 сентября 2011

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

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

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

aaaa
11111-----

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

Ответы [ 6 ]

887 голосов
/ 27 мая 2012

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

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

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

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

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

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

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

82 голосов
/ 15 сентября 2011

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

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

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

while read line; do    
    echo $line    
done < file.txt
69 голосов

Две важные ловушки

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

  1. Удаление завершающего перевода строки из расширения команды
  2. Удаление символов 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: добавьте дополнительный символ в расширение команды и удалите его позже:

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

Выходы:

0000000 61 0a 0a
0000003

Практически POSIX обходной путь: ASCII-кодирование. Смотри ниже.

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

Не существует нормального способа Bash для хранения NUL-символов в переменных .

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

Пример: * +1051 *

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 $"" литералы:

    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) ... Я не вижу альтернативы POSIX 7 для расширения процесса bash <(), за исключением расширения запись в другой файл ...

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

3 голосов
/ 31 августа 2018

это работает для меня: v=$(cat <file_path>) echo $v

2 голосов
/ 29 марта 2018

Как Сиро Сантилли отмечает , используя подстановки команд, удаляются завершающие символы новой строки.Их обходной путь, заключающийся в добавлении завершающих символов, великолепен, но после его использования в течение некоторого времени я решил, что мне нужно решение, которое вообще не использует подстановку команд.

Мой подход теперь использует read вместе с printf встроенным -v флагом для чтения содержимого стандартного ввода непосредственно в переменную.

# Reads stdin into a variable, accounting for trailing newlines. Avoids needing a subshell or
# command substitution.
read_input() {
  # Use unusual variable names to avoid colliding with a variable name
  # the user might pass in (notably "contents")
  : "${1:?Must provide a variable to read into}"
  if [[ "$1" == '_line' || "$1" == '_contents' ]]; then
    echo "Cannot store contents to $1, use a different name." >&2
    return 1
  fi

  local _line _contents
   while read -r _line; do
     _contents="${_contents}${_line}"$'\n'
   done
   _contents="${_contents}${_line}" # capture any content after the last newline
   printf -v "$1" '%s' "$_contents"
}

Поддерживает входные данные с завершающими символами новой строки или без них.

Пример использования:

$ read_input file_contents < /tmp/file
# $file_contents now contains the contents of /tmp/file
0 голосов
/ 17 декабря 2018

Вы можете получить доступ к 1 строке за один раз для цикла

#!/bin/bash -eu

#This script prints contents of /etc/passwd line by line

FILENAME='/etc/passwd'
I=0
for LN in $(cat $FILENAME)
do
    echo "Line number $((I++)) -->  $LN"
done

Копирование всего содержимого в файл (скажем, line.sh); Выполнить

chmod +x line.sh
./line.sh
...