Я хочу прочитать файл и сохранить его в переменной, но мне нужно сохранить переменную, а не просто распечатать файл. Как я могу это сделать? Я написал этот скрипт, но это не совсем то, что мне нужно:
#!/bin/sh
while read LINE
do
echo $LINE
done <$1
echo 11111-----------
echo $LINE
В моем скрипте я могу задать имя файла в качестве параметра, так что, если файл содержит "aaaa", например, он выведет следующее:
aaaa
11111-----
Но это просто выводит файл на экран, а я хочу сохранить его в переменной! Есть ли простой способ сделать это?
В кроссплатформенных системах используется наименьший общий знаменатель sh
:
#!/bin/sh
value=`cat config.txt`
echo "$value"
В bash
или zsh
, чтобы прочитать весь файл в переменную без вызова cat
:
#!/bin/bash
value=$(<config.txt)
echo "$value"
Вызов cat
в bash
или zsh
для считывания файла будет считаться Бесполезным использованием Cat.
Обратите внимание, что для сохранения новых строк не обязательно заключать подстановку команды в кавычки.
Два важных подводных камня
которые до сих пор игнорировались другими ответами:
Удаление концевой новой строки из расширения команды.
Это проблема для:
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
решения, и я не знаю никакого хорошего обходного пути для этого.
Пример:
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.
это работает для меня:
в=$(кошка <путь_к_файлу>) эхо $в
Как Чиро Сантилли Примечания, используя команду подстановки упадет конечные символы новой строки. Их обходной путь, добавив символы-это здорово, но после использования его в течение некоторого времени я решил, что мне нужно решение, которое не'т использовать подстановку команд вообще.
Свой подход использует читать
вместе с е
встроенный'ы флаг-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
Вы можете получить доступ к 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 ) ; выполнение
чмод +х line.sh ./line.sh