locationfile.txt
と呼ばれるさまざまなファイルの場所を含むファイルがあります。
/home/user/documents/file1
/home/user/file2
/home/user/file3
各テキストファイルには約10行あり、すべてのファイルをfinalfile
という別のファイルに出力したいと思います。
私がそうするなら私はそれを理解しました
cat locationfile.txt | while read line; do cat "$line"; done
これにより、すべてのファイルが一緒に印刷されるため、最大30行のテキストファイルになります。
私の質問は、次のように、テキストの間にファイルの場所を含めて、そのファイルを出力する方法を教えてください。
/home/user/documents/file1
text
text
text
""
/home/user/file2
text
text
text
""
/home/user/file3
text
text
text
""
cat
の前に、ファイル名のecho
またはprintf
を実行します。
while read line; do printf '%s\n' "$line"; cat "$line"; done <locationfile.txt >finalfile
または、より読みやすく:
while read line; do
printf '%s\n' "$line"
cat "$line"
done <locationfile.txt >finalfile
これには、locationfile.txt
内のすべてのパスに\
が含まれていない必要があることに注意してください。
パス名に\
を含めることは非常に珍しいことですが、より安全です。
while read -r line; do
printf '%s\n' "$line"
cat "$line"
done <locationfile.txt >finalfile
...同じ制限はありません。
ファイルも実際に存在することを確認するためのチェックを追加し、存在しない場合は警告を出力します。
while read -r line; do
if [[ -f "$line" ]]; then
printf '%s\n' "$line"
cat "$line"
else
printf 'Warning: "%s" does not exist\n' "$line" >&2
fi
done <locationfile.txt >finalfile
ここでの「存在しない」とは、「少なくとも通常のファイルではない」という意味です。
そこにも""
が必要な場合は、一種のファイルの終わり-コンテンツ-マーカーとして、cat
の後にそれを入れてください。
while read -r line; do
if [[ -f "$line" ]]; then
printf '%s\n' "$line"
cat "$line"
echo '""'
else
printf 'Warning: "%s" does not exist\n' "$line" >&2
fi
done <locationfile.txt >finalfile
そして最後に、自己文書化の名前を付けます。
while read -r file_path; do
if [[ -f "$file_path" ]]; then
printf '%s\n' "$file_path"
cat "$file_path"
echo '""'
else
printf 'Warning: "%s" does not exist\n' "$file_path" >&2
fi
done <file_paths.txt >files_concat_with_paths.txt
while read file; do
( echo "$file"; cat "$file"; echo '"""' ) >> /path/to/outputfile
done < /path/to/filelist
GNU awkがある場合、シェルループを完全に回避するオプションは次のとおりです。
xargs -d '\n' -a locationfile.txt gawk 'BEGINFILE{print FILENAME} 1' > newfile
またはGNU sed:
xargs -d '\n' -a locationfile.txt sed -s '1F' > newfile
ファイル名の正確な形式を気にしない場合は、このトリックをhead
で使用できます。
xargs -d '\n' -a locationfile.txt head -vn -0 > newfile
(-n -0
はhead
に、最初の0を除くすべての行(つまり、すべての行)を出力するように指示します。
別のPerlアプローチ:
$ Perl -le 'do{print $_,`cat $_`,"\"\"";} for <>' locationfile.txt
text
text
text
""
text
text
text
""
text
text
text
""
これはPerlの魔法を少し使っているので、説明が必要かもしれません。 -l
は、各入力行から末尾の改行を削除し、各print
呼び出しに改行を追加します。 -e
は、スクリプトをPerl
に与える方法です。
さて、<>
は「配列に分割された入力ファイルの内容」です。したがって、do {foo} for <>
は入力ファイルの各行でfoo
を実行します。そのfor
ループ内で、特別な変数$_
が各入力行に割り当てられます。 `command`
構文は、デフォルトのシェル(システムコール)でcommand
を実行し、その出力を返します。したがって、cat $_
はlocationfile.txt
内の各ファイル名をcat
します。
全体として、print $_,`cat $_`,"\"\""
はファイル名(現在は$_
にあります)、cat
(ファイルの内容)の結果、次に""
を出力します(ただし、エスケープする必要があります)それらなので、\"\"
)。
言い換えると、そのスクリプトは、「各入力行を読み取り、それを印刷し、そこに含まれるファイルをcat
し、各ファイルの後に""
を印刷することを意味します。
ループにecho "$line"
を追加するだけです。
cat locationfile.txt | while read line; do echo "$line" && cat "$line"; done
system()
関数を介したAWKアプローチ。 sprintf
を介してコマンド文字列を作成し、それをsystem()
に渡します。
$ awk '{print $0; cmd=sprintf("cat \"%s\"",$0);system(cmd); print "\"\""}' filenames.txt
/home/xieerqi/input1.txt
Hello, I am input file #1
And this is second line of the text
""
/home/xieerqi/input2.txt
And I am input file #2
Roses are red, violets are blue
AWK is awesome, Python is too
""
/etc/resolv.conf
# Dynamic resolv.conf(5) file for glibc resolver(3) generated by resolvconf(8)
# DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE OVERWRITTEN
nameserver 127.0.1.1
""
そしてPerlのアプローチですが、コード内でファイルを開くことで:
$ Perl -ne 'chomp; open($fh,$_) or die;print "$_\n";while($line = <$fh>){ print $line; print "\"\"\n" if eof}' filenames>
/home/xieerqi/input1.txt
Hello, I am input file #1
And this is second line of the text
""
/home/xieerqi/input2.txt
And I am input file #2
Roses are red, violets are blue
AWK is awesome, Python is too
""
/etc/resolv.conf
# Dynamic resolv.conf(5) file for glibc resolver(3) generated by resolvconf(8)
# DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE OVERWRITTEN
nameserver 127.0.1.1
""