쉘에서 변수로 파일을 읽는 방법은 무엇입니까?


489

파일을 읽고 변수에 저장하고 싶지만 파일을 인쇄하지 않고 변수를 유지해야합니다. 어떻게해야합니까? 이 스크립트를 작성했지만 필요한 것은 아닙니다.

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

스크립트에서 파일 이름을 매개 변수로 제공 할 수 있으므로 파일에 "aaaa"가 포함 된 경우 다음과 같이 인쇄됩니다.

aaaa
11111-----

그러나 이것은 단지 파일을 화면에 출력하고 변수에 저장하고 싶습니다! 이 작업을 수행하는 쉬운 방법이 있습니까?


1
일반 텍스트 인 것 같습니다. 이 바이너리 파일이 있었다면, 당신은 필요 의 결과로, cat또는 $(<someFile)(크기가 실제 파일보다 작) 불완전한 결과가 유도된다.
물병 자리 힘

답변:


1052

크로스 플랫폼 최저 공통 분모에서 sh다음을 사용합니다.

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

에서 bash또는 zsh, 호출하지 않고 변수로 전체 파일을 읽을 수 있습니다 cat:

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

호출 catbash또는 zsh파일을 후루룩 소리 내며 먹기하는 것이 고려 될 것이다 고양이의 쓸모없는 사용 .

줄 바꿈을 유지하기 위해 명령 대체를 인용 할 필요는 없습니다.

Bash Hacker의 Wiki- 명령 대체-전문 분야를 참조하십시오 .


4
좋아,하지만 배쉬입니다. 모든 경우에 맞지 않을 수 있습니다.
moala

14
하지 않을까요 value="`cat config.txt`"value="$(<config.txt)"config.txt 파일에 공백이 포함 된 경우에 더 안전?
Martin von Wittich

13
cat위와 같이 사용 하는 것이 항상 쓸모없는 사용으로 간주되는 것은 아닙니다 cat. 예를 들어, < invalid-file 2>/dev/null라우팅 할 수 없다는 오류 메시지가 발생합니다 /dev/null반면, cat invalid-file 2>/dev/null제대로 라우팅 얻을 않습니다 /dev/null.
Dejay Clayton

16
나와 같은 새로운 쉘 스크립터의 경우 고양이 버전은 작은 따옴표가 아닌 백 틱을 사용합니다. 잘하면 이것은 누군가를 알아내는 데 30 분을 절약 할 것입니다.
ericksonla

7
나와 같은 새로운 bashers의 경우 : 그것은 value=$(<config.txt)좋지만 value = $(<config.txt)나쁜 것입니다. 그 공간을 조심하십시오.
ArtHare

88

전체 파일을 변수로 읽으려면 다음을 수행하십시오.

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

한 줄씩 읽으려면 다음을 수행하십시오.

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

2
@brain : 파일이 Config.cpp이고 백 슬래시를 포함하면 어떻게됩니까? 큰 따옴표와 따옴표?
user2284570

2
에 변수를 큰 따옴표로 묶어야합니다 echo "$value". 그렇지 않으면 쉘은 값에 공백 토큰 화 및 와일드 카드 확장을 수행합니다.
tripleee

3
@ user2284570 언급 한 이상한 레거시 동작이 특별히 요구되지 않는 한 항상 사용 read -r하는 것이 아니라 read항상 사용하십시오 .
tripleee

74

두 가지 중요한 함정

지금까지 다른 답변에서 무시되었습니다.

  1. 명령 확장에서 후행 줄 바꿈 제거
  2. 널 문자 제거

명령 확장에서 후행 줄 바꿈 제거

이것은 다음에 대한 문제입니다.

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 방법은 없습니다 .

이는 확장과 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를 사용하지 마십시오.


2
줄 바꿈이 필요했기 때문에이 하나만 감사했습니다.
Jason Livesay

1
@CiroSantilli : FILE이 Config.cpp이고 백 슬래시를 포함하면 어떨까요? 큰 따옴표와 따옴표?
user2284570

@ user2284570 몰랐지만 쉽게 찾을 수 S="$(printf "\\\'\"")"; echo $S있습니다.. 출력 : \'". 그래서 그것은 작동합니다 =)
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

@CiroSantilli : 5511 라인에서? 자동화 된 방법이 없습니까?
user2284570

5511 줄이 어디 있는지 이해가 안됩니까? 함정은 $()확장 에서 비롯된 것이며 , 나의 예는 $()확장이에서 작동 한다는 것을 보여줍니다 \'".
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功


2

치로 틸리 노트 후행 줄 바꿈을 삭제합니다 명령 대체를 사용하여. 후행 문자를 추가하는 해결 방법은 훌륭하지만 꽤 오랫동안 사용한 후에 명령 대체를 전혀 사용하지 않는 솔루션이 필요하다고 결정했습니다.

내 접근 방식은 이제 stdin의 내용을 변수로 직접 읽기 위해 내장 플래그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

큰! _contents="${_contents}${_line}\n "개행을 유지 하는 것과 같은 것을 사용하지 않는 이유가 궁금합니다 .
Eenoku

1
당신에 대해 묻는가 $'\n'? 필요합니다. 그렇지 않으면 리터럴 \ n문자를 추가하는 것 입니다. 코드 블록에는 끝에 여분의 공간이 있습니다. 의도적인지 확실하지 않지만 이후의 모든 줄에는 여분의 공백이 들여 쓰기됩니다.
dimo414

설명 감사합니다!
Eenoku

-3

for 루프로 한 번에 한 줄에 액세스 할 수 있습니다

#!/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

전체 내용을 File (예 : line.sh)에 복사하십시오. 실행

chmod +x line.sh
./line.sh

귀하의 for루프 라인을 통해 루프하지 않습니다, 그것은 단어 이상 반복합니다. 의 경우 /etc/passwd각 줄에 한 단어 만 포함됩니다. 그러나 다른 파일에는 한 줄에 여러 단어가 포함될 수 있습니다.
mpb
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.