How to read a file into a variable in shell?
我想读取一个文件并将其保存在变量中,但我需要保留变量,而不仅仅是打印出文件。我该怎么做?我写了这个脚本,但它不是我需要的:
1 2 3 4 5 6 7 | #!/bin/sh while read LINE do echo $LINE done <$1 echo 11111----------- echo $LINE |
在我的脚本中,我可以将文件名作为参数,因此,如果文件包含"a a a a",例如,它将打印出以下内容:
1 2 | aaaa 11111----- |
但这只是把文件打印到屏幕上,我想把它保存到一个变量中!有简单的方法吗?
在跨平台中,您使用的最小公分母
1 2 3 | #!/bin/sh value=`cat config.txt` echo"$value" |
在
1 2 3 | #!/bin/bash value=$(<config.txt) echo"$value" |
在
注意,不需要引用命令替换来保留换行符。
参见:bash hacker's wiki-命令替换-专业。
如果要将整个文件读取到变量中:
1 2 3 | #!/bin/bash value=`cat sources.xml` echo $value |
如果要逐行阅读:
1 2 3 | while read line; do echo $line done < file.txt |
两个重要的陷阱
到目前为止,其他答案都忽略了这一点:
从命令扩展中删除尾随换行符
这是一个问题:
1 | value="$(cat config.txt)" |
类型解决方案,但不适用于基于
命令扩展删除尾随换行符:
1 2 3 | S="$(printf"a ")" printf"$S" | od -tx1 |
输出:
1 2 | 0000000 61 0000001 |
这打破了从文件中读取的简单方法:
1 2 3 4 5 6 7 | FILE="$(mktemp)" printf"a ">"$FILE" S="$(<"$FILE")" printf"$S" | od -tx1 rm"$FILE" |
POSIX解决方法:在命令扩展中附加一个额外的字符,稍后将其删除:
1 2 3 | S="$(cat $FILE; printf a)" S="${S%a}" printf"$S" | od -tx1 |
输出:
1 2 | 0000000 61 0a 0a 0000003 |
几乎是POSIX解决方案:ASCII编码。见下文。
去除nul字符
在变量中存储nul字符没有明智的bash方法。
这会影响到扩展和
例子:
1 2 3 | printf"a\0b" | od -tx1 S="$(printf"a\0b")" printf"$S" | od -tx1 |
输出:
1 2 3 4 5 | 0000000 61 00 62 0000003 0000000 61 62 0000002 |
哈,我们的屁股不见了!
解决办法:
ASCII编码。见下文。
使用bash扩展
$"" 字面值:1
2S=$"a\0b"
printf"$S" | od -tx1只适用于文本,因此不适用于从文件中读取。
解决陷阱
在变量中存储文件的Uuencode base64编码版本,并在每次使用前解码:
1 2 3 4 5 6 | FILE="$(mktemp)" printf"a\0 ">"$FILE" S="$(uuencode -m"$FILE" /dev/stdout)" uudecode -o /dev/stdout <(printf"$S") | od -tx1 rm"$FILE" |
输出:
1 2 | 0000000 61 00 0a 0000003 |
uuencode和udecode是posix 7,但默认情况下不在Ubuntu 12.04中(
当然,这是缓慢和不方便的,所以我想真正的答案是:如果输入文件可能包含nul字符,不要使用bash。
这对我很有用:
v=$(cat
echo $v
正如Ciro Santilli所指出的,使用命令替换将删除尾随的换行符。他们添加尾随字符的解决方案非常好,但是在使用了一段时间之后,我决定需要一个根本不使用命令替换的解决方案。
我的方法现在使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | # 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}"$' ' done _contents="${_contents}${_line}" # capture any content after the last newline printf -v"$1" '%s'"$_contents" } |
这支持带或不带尾随新行的输入。
示例用法:
1 2 | $ read_input file_contents < /tmp/file # $file_contents now contains the contents of /tmp/file |
您可以通过for循环一次访问一行
1 2 3 4 5 6 7 8 9 10 | #!/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);执行
1 2 | chmod +x line.sh ./line.sh |