在shell中将输出写入多个文件

3
我有一个名为File_A的文件,其中包含了135个文档,每个文档都是一行长文本。同时,我还有一个名为File_B的文件,其中包含了15个短语。我需要在File_A中寻找包含File_B中某个短语的句子及其之前的内容,并将这些句子从File_A中提取出来并输出到新文件File_1、File_2等中,直到所有匹配的句子都被提取出来。我使用以下代码实现了这个功能。请注意,代码中保留了HTML标记。
i=1
while read line; do
 while read row; do
   cat "$line" | sed 's/\./.\n/g' | grep -i -B 1 "$row"  | tr -d '\n' |  sed 's/--/\n/g'    >> file_$i
 done < $2 
 $i = $i+1;
done < $1

这里的问题是,输出被打印到控制台而不是新文件中。请有人帮助我认识到我的错误。
谢谢。

你的意思是你的代码实际上可以运行,但将输出打印到控制台吗?我原以为cat "$line"会立即失败,因为$line是文本中的一行而不是文件名。 - undefined
我尝试了你的代码,但它只是输出一系列错误,如cat: something something...: 没有该文件或目录命令未找到 - undefined
5个回答

2

这个清晰吗?如果不清楚,请评论并我会编辑它。 Bash输出重定向示例:

echo "some text" >file.txt;
#here we add on to the end of the file instead of overwriting the file
echo "some additional text" >>file.txt;
#put something in two files and output it
echo "two files and console" | tee file1.txt | tee file2.txt;
#put something in two files and output nothing
echo "just two files" | tee file1.txt >file2.txt;

1

我认为这会起作用

i=1
while read line; do
 while read row; do
   echo "$line" | sed 's/\./.\n/g' | grep -i -B 1 "$row"  | tr -d '\n' |  sed 's/--/\n/g' >> file_$i
 done < $2 
 $i = $i+1;
done < $1 

a=0 
while read line; do 
a=$(($a+1)); 
while read row; do
    echo "$line" | sed 's/\./.\n/g' | grep -i -B 1 "$row" | tr -d '\n' | sed 's/--/\n/g' >> file_$a done < $2 done < $1

嗨,谢谢你的快速回复,但是它还是不起作用。输出在控制台上,而不是在一个新文件中。 - undefined
你有$line可能是什么的例子吗? - undefined
这是一份长文本,实际上是一份事故报告。该报告被整理成了一行。 - undefined
此代码实际上正在创建多个文件,但我认为cat命令有些错误。它在控制台上显示文本,并且新文件没有输出。a=0 while read line; do a=$(($a+1)); while read row; do < "$line" | sed 's/./.\n/g' | grep -i -B 1 "$row" | tr -d '\n' | sed 's/--/\n/g' >> file_$a done < $2 done < $1 - undefined
如果$line是一行文本而不是文件,这行代码对我有效:echo "$line" | sed 's/./.\n/g' | grep -i -B 1 "$row" | tr -d '\n' | sed 's/--/\n/g' >> file_$a - undefined
谢谢 Jsoft,第二个方法非常好用,我应该使用 echo 而不是 cat :) - undefined

1

这不是在 shell 中增加变量的方法:

$i = $i + 1

相反,它尝试运行一个命令,其名称是$i的当前值。你需要这个:

let i=i+1

或者更简洁地说,
let i+=1

这可能不是问题,但它是一个问题,它可能会导致奇怪的行为。
我看到的唯一另一件事是你的文件名周围缺少引号("$1""$2")。
此外,如果每行都是一个文件名,你不需要使用cat;只需执行
<"$line" sed ...

如果每行都是文件的内容而不是名称,那么使用cat命令就完全错误了,因为它试图找到一个名字为那个长文本的文件。你可以使用这个命令代替:
<<<"$line" sed ...

编辑 此外,如果fileB中的行数不是很多,您可能可以避免为fileA中列出的每个文件重复读取它。只需一次性将fileB中的所有内容读入内存即可:

IFS=$'\n' rows=($(<"$2"))
let i=0
while read line; do
  for row in "${rows[@]}"; do
    <<<"$line" sed 's/\./.\n/g' | grep -i -B 1 "$row"  | 
             tr -d '\n' |  sed 's/--/\n/g' >> file_$i
  done 
  let i+=1
done < "$1"

实际上,您甚至可以在单个grep中完成它:

pat=''
while read row; do
  pat="${pat:+$pat|}$row"
done <"$2"

let i=0
while read line; do
  <<<"$line" sed 's/\./.\n/g' | egrep -i -B 1 "$pat"  | 
             tr -d '\n' |  sed 's/--/\n/g' >"file_$i"
let i+=1
done < "$1"

谢谢,我意识到应该是像你说的那样。 - undefined
a = 0 while read line; do a=$(($a+1)); while read row; do < "$line" | sed 's/./.\n/g' | grep -i -B 1 "$row" | tr -d '\n' | sed 's/--/\n/g' >> file_$a done < $2 done < $1 - undefined
谢谢Reed,我的File_B中有大约10个短语,我会按照你的第二段代码进行操作。感谢你让我意识到了在Shell脚本中的自增操作。 - undefined

1
修复先前提到的问题(重新递增i和误用cat)会导致类似以下的结果。请注意,行date > file_$i用于调试,以确保每个测试开始时输出文件都是新的。冒号运算符是一个无操作符。形式<<<引入了“here-doc”。如果$lines的内容是文件名,而不是问题中指定的文档,请在<<<"$lines"的位置使用<"$lines"
#!/bin/bash
i=1
while read line; do
    date > file_$i
    while read row; do
    sed 's/\./.\n/g' <<< "$line" | grep -iB1 "$row" | tr -d '\n' |  sed 's/--/\n/g' >> file_$i
    done < $2 
    : $((i++))
done < $1

假设splitdoc.data包含以下内容:

This is doc 1.  I am 1 fine.  How are you, 1.? Ok. Hello 1.--  Go away now.
This is doc 2.  I am 2 fine.  How are you, 2.? Ok. Hello 2.--  Go away now.
This is doc 3.  I am 3 fine.  How are you, 3.? Ok. Hello 3.--  Go away now.
This is doc 4.  I am 4 fine.  How are you, 4.? Ok. Hello 4.--  Go away now. 

并使用以下方式拆分splitdoc.tags:

How are you
Go away now

然后输入命令

./splitdoc.sh splitdoc.data splitdoc.tags ; head file_*

生成:

==> file_1 <==
Fri Oct 26 19:42:00 MDT 2012
  I am 1 fine.  How are you, 1. Hello 1.
  Go away now.
==> file_2 <==
Fri Oct 26 19:42:00 MDT 2012
  I am 2 fine.  How are you, 2. Hello 2.
  Go away now.
==> file_3 <==
Fri Oct 26 19:42:00 MDT 2012
  I am 3 fine.  How are you, 3. Hello 3.
  Go away now.

我相信$1中的行应该是要被grep的文件名,而不是内容本身,所以<<<是不合适的。 - undefined
@MarkReed,问题中提到每一行都是一个“文档”,这个说法有些模糊,但正如你所建议的,它可能是一个文件名而不是一个文档。我在代码前面添加了一个注释。 - undefined
每行都是一个长文本,实际上是一份事故报告。每个报告都被制成一行。 - undefined
请问这个命令中的head file_是什么意思:./splitdoc.sh splitdoc.data splitdoc.tags; head file_ - undefined
@Santosh,如果不带任何选项,head命令会打印出在命令行上列出的文件的文件名和前10行内容。(如果只有一个文件被列出,则不会打印文件名。)请参阅man head - undefined

1

tee 实际上接受多个文件参数,因此非常简单:

# from file
tee 1.txt 2.txt 3.txt <0.txt

# from string
tee 1.txt 2.txt 3.txt <<<'text'

# from heredoc
tee 1.txt 2.txt 3.txt <<'EOF'
line
line
line
EOF

# from pipeline
command | tee 1.txt 2.txt 3.txt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接