파일에서 읽은 변수가 정렬되지 않았으며 추가 줄이 있습니다.

파일에서 읽은 변수가 정렬되지 않았으며 추가 줄이 있습니다.
#!/bin/bash

FILES=/tmp/files.txt
FIELDNAME=/tmp/fieldname.txt

num=$(wc -l < $FIELDNAME)

#to read fieldname.txt content
FILE1=$1
cat $FILE1 > FILE2
value=$(<FILE2)

#to create empty lines
yes '' | sed $num\q >> $FILES

#to add fieldname content into files.txt
I=0
for fieldname in $value
do
    echo "Line number $((I++)) --> $fieldname"
    sed -i -e "i\input $fieldname " $FILES
    sed -i -e 's/^/    /' $FILES
    #to remove empty lines
    sed -i '/^[[:space:]]*$/d' $FILES
done

sed -i '/^[[:space:]]*$/d' $FILES

내 스크립트 이름은 script.sh이고 이것이 스크립트를 호출하는 방법입니다.

./script.sh fieldname.txt

예상되는 결과는 다음과 같습니다.

    input abc
    input def
    input ghi

그러나 내가 얻은 출력은 정렬되지 않았으며 다음과 같이 3줄 이상입니다.

    input ghi
        input def
    input ghi
            input abc
    input ghi
        input def
    input ghi
            input abc
    input ghi
        input def
    input ghi
            input abc

답변1

작동 방식을 오해하는 것 같습니다 sed. 파일에서 sed 명령을 실행하면 전체 파일을 읽고 편집 규칙을 적용합니다.모든줄(규칙에 적용 위치를 제한하는 "주소" 줄이 없는 경우) 따라서 귀하의 예에서는 세 개의 빈 줄만 있는 파일로 시작한 다음 sed -i -e "i\input $fieldname "해당 파일을 실행하면 "enter abc"와 같이 파일 앞에 줄이 추가됩니다.이 세 줄은 각각. 따라서 다음과 같은 파일이 있습니다.

input abc 

input abc 

input abc 

(안보이지만 끝에 빈줄이 있습니다.) 그들은, 당신이 실행하면 sed -i -e 's/^/ /'앞에 공백 4개가 추가됩니다.모든줄(빈 줄 포함):

    input abc 

    input abc 

    input abc 

그런 다음 실행하면 sed -i '/^[[:space:]]*$/d'실제로 예상한 대로 수행됩니다. 공백만 있는 줄을 제거하고 다음을 남깁니다.

    input abc 
    input abc 
    input abc 

그런 다음 루프의 다음 반복에서 run 을 실행하면 sed -i -e "i\input def "기존 각 행 앞에 새 행이 다시 배치됩니다.

input def 
    input abc 
input def 
    input abc 
input def 
    input abc 

그런 다음 sed -i -e 's/^/ /'각 줄에 다시 4개의 공백을 추가합니다(이미 공백이 있는 줄 포함).

    input def 
        input abc 
    input def 
        input abc 
    input def 
        input abc 

...등. 이건 네가 하고 싶은 일을 하는 게 아니야별말씀을요.

당신이하려는 일을 내가 이해한다면 sed그것은 실제로 작업에 적합한 도구가 아닙니다. 기존 파일을 편집하려고 하기보다는 새 파일을 만들어 한 줄씩 추가하고 싶은 것 같습니다. 다음과 같이 쉽게 이 작업을 수행할 수 있습니다.

: >"$FILES"    # This empties the file (in case there's something there from last run)

I=0
for fieldname in $value
do
    echo "Line number $((I++)) --> $fieldname"
    echo "    input $fieldname" >>"$FILES"    # Append a line to the end of the file
done

"줄 번호..." 항목을 인쇄할 필요가 없거나 stdout 대신 stderr로 보낼 수 있는 경우(실제 오류가 아니더라도 일반적으로 상태 정보를 보내야 하는 곳) 다음을 수행할 수 있습니다. 훨씬 더 간단합니다:

I=0
for fieldname in $value
do
    echo "Line number $((I++)) --> $fieldname" >&2    # The >&2 redirects to standard error
    echo "    input $fieldname"
done >"$FILES"    # Just send *all* standard output from the loop into the file

두 경우 모두 파일 num의 출력이나 사전 로드가 필요하지 않습니다 .yes

여기에는 겉보기에 나쁜 관행이 많이 있습니다. 우선, 변수 참조는 "$FILES"위의 예처럼 거의 항상 큰따옴표로 묶어야 합니다. 이렇게 하면 실수로 여러 "단어"로 분할되거나 파일 이름 와일드카드로 확장되는 것을 방지할 수 있습니다. 나는 추천한다shellcheck.net이와 같은 일반적인 실수를 지적하십시오.

$value( )를 사용하여 이 작업을 수행하지 않았다는 점에 유의하십시오. for fieldname in $value이 경우 변수 값을 단어로 분할하기 위해 쉘에 의존하기 때문입니다... 이는 특히 안전하지 않습니다. 정말 반복하고 싶니?성격입력 파일에 있거나 루프를 원합니다.철사대신에? 줄을 원하면 해당 for ... in구성을 사용하지 말고 read루프를 사용하십시오.

I=0
while read fieldname
do
    echo "Line number $((I++)) --> $fieldname" >&2
    echo "    input $fieldname"
done <"$FILE1" >"$FILES"    # Read input from $FILE1, write output to $FILES

바라보다BashFAQ #001: "파일(데이터 스트림, 변수)을 한 줄씩(및/또는 필드별로) 어떻게 읽나요?"더 많은 정보를 알고 싶습니다.

@Kusalananda가 (현재 삭제된) 주석에서 지적했듯이 이 작업을 한 줄씩 수행하고 "줄 번호..." 출력이 전혀 필요하지 않은 경우 sed셸 루프에서는 사용할 수 없습니다. 자신을 얻으십시오 sed입력 파일을 스캔하여 input각 줄에 ""를 추가하십시오.

sed 's/^/    input /' "$FILE1" >"$FILES"

어쨌든 현재 $FILE1을 문자 그대로 "FILE2"라는 파일에 복사한 다음 이를 value변수라는 이름으로 읽고 있습니다. 이 중 어떤 작업도 원본 파일에서 직접 읽어야 할 필요는 없습니다.

또한 참고: 모두 대문자 이름 대신 소문자 또는 대소문자 혼합 변수 이름을 사용하십시오. 쉘 및/또는 기타 도구에 특별한 의미를 갖는 전체 대문자 이름이 많이 있으며, 그 중 하나를 잘못 사용하면 이상한 일이 발생할 수 있습니다.

관련 정보