web-dev-qa-db-ja.com

シェルで変数にファイルを読む方法は?

ファイルを読み、それを変数に保存したいのですが、ファイルを出力するだけでなく、変数を保持する必要があります。これどうやってするの?私はこのスクリプトを書きましたが、私が必要としていたものではありません。

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

私のスクリプトでは、ファイル名をパラメータとして指定できます。たとえば、ファイルに "aaaa"が含まれている場合は、次のように出力されます。

aaaa
11111-----

しかし、これは単にファイルを画面に表示するだけなので、それを変数に保存したいのです。これを行う簡単な方法はありますか?

391
kaka

クロスプラットフォームの最小公倍数shでは、次のように使用します。

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

bashまたはzshで、catを呼び出さずにファイル全体を変数に読み込むには、次のようにします。

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

ファイルを丸呑みするためにcatまたはbashzshを呼び出すことは、 Catの無駄な使用 と見なされます。

改行を保存するためにコマンド置換を引用符で囲む必要はないことに注意してください。

Bash Hacker's Wiki - コマンド置換 - Specialties を参照してください。

859
Alan Gutierrez

ファイル全体を変数に読み込む場合は、次のようにします。

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

1行ずつ読みたい場合は、

while read line; do    
    echo $line    
done < file.txt
80
brain

2つの重要な落とし穴

これまでのところ他の答えでは無視されていました:

  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文字の削除

変数にNUL文字を格納するための正しいBash方法はありません

これは拡張と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パッケージ)... bashプロセスの代わりのPOSIX 7代替の<()置換拡張子は他のファイルに書き込む以外はありません...

もちろん、これは遅くて不便なので、本当の答えは、入力ファイルにNUL文字が含まれる可能性がある場合はBashを使用しないことです。

これは私のために働く:v=$(cat <file_path>) echo $v

2
angelo.mastro

As Ciro Santilli氏が としてコマンド置換を使用すると、末尾の改行が削除されます。末尾の文字を追加するという回避策は素晴らしいですが、しばらくの間それを使用した後、コマンド置換をまったく使用しない解決策が必要であると判断しました。

私のアプローチ は、stdinの内容を直接変数に読み込むために、read組み込みの -vフラグ と共にprintfを使用するようになりました。

# 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
2
dimo414