【问题标题】:Shell Script - list files, read files and write data to new fileShell 脚本 - 列出文件、读取文件并将数据写入新文件
【发布时间】:2013-03-26 10:12:21
【问题描述】:

我有一个关于 shell 脚本的特殊问题。
简单的脚本编写对我来说没问题,但我是新手,想把我做成一个简单的数据库文件。

所以,我想做的是:

- Search for filetypes (i.e. .nfo) <-- should be no problem :)
- read inside of each found file and use some strings inside
- these string of each file should be written in a new file. Each found file informations 

应该是新文件中的一行

我希望我能很好地解释我的“项目”。

我现在的问题是,要了解如何告诉脚本它必须搜索文件,然后使用每个文件来读取它,并使用其中的一些信息将其写入新文件。

我会解释得更好。
我正在搜索文件并返回:

file1.nfo
文件 2.nfo
文件3.nfo

好的,现在在每个文件中,我需要两行之间的信息。即
file1.nfo:

<user>test1</user>

file2.nfo:

<user>test2</user>

所以在新文件中现在应该有:

file1.nfo:user1
file2.nfo:user2

好吧:

find -name *.nfo  > /test/database.txt

正在打印文件列表。 和

sed -n '/<user*/,/<\/user>/p' file1.nfo

给我完整的文件,而不仅仅是&lt;user&gt;和&lt;/user&gt;之间的信息

我试着一步一步往前走,我读了很多书,但似乎很难。

我做错了什么,列出所有文件并将文件和两个字符串之间的内容写入文件的最佳方式应该是什么?

新编辑:

好的,这里有更多信息的更新。 我现在学到了很多东西,并在网上搜索了我的问题。我可以找到很多信息,但我不知道如何将它们放在一起以便我可以使用它。

现在使用 awk 是我返回文件名和字符串。

现在这里是完整的信息(我想我可以在一些帮助下自己继续,但我不能:()

这里是一个例子:/test/file1.nfo

<string1>STRING 1</string1>
<string2>STRING 2</string2>
<string3>STRING 3</string3>
<string4>STRING 4</string4>
<personal informations>
<hobby>Baseball</hobby>
<hobby>Baskeball</hobby>
</personal informations>

这里是 /test/file2.nof 的例子

<string1>STRING 1</string1>
<string2>STRING 2</string2>
<string3>STRING 3</string3>
<string4>STRING 4</string4>
<personal informations>
<hobby>Soccer</hobby>
<hobby>Traveling</hobby>
</personal informations>

我要创建的文件必须是这样的。

STRING 1:::/test/file1.nfo:::Date of file:::STRING 4:::STRING 3:::Baseball, Basketball:::STRING 2
STRING 1:::/test/file2.nfo:::Date of file:::STRING 4:::STRING 3:::Baseball, Basketball:::STRING 2

“文件日期”应该是文件的创建日期。这样我就可以看到文件的年龄了。

所以,这就是我需要的,而且似乎并不容易。

非常感谢。

更新错误 -printf

find: unrecognized: -printf

Usage: find [PATH]... [OPTIONS] [ACTIONS]

Search for files and perform actions on them.
First failed action stops processing of current file.
Defaults: PATH is current directory, action is '-print'

    -follow         Follow symlinks
    -xdev           Don't descend directories on other filesystems
    -maxdepth N     Descend at most N levels. -maxdepth 0 applies
                    actions to command line arguments only
    -mindepth N     Don't act on first N levels
    -depth          Act on directory *after* traversing it

Actions:
    ( ACTIONS )     Group actions for -o / -a
    ! ACT           Invert ACT's success/failure
    ACT1 [-a] ACT2  If ACT1 fails, stop, else do ACT2
    ACT1 -o ACT2    If ACT1 succeeds, stop, else do ACT2
                    Note: -a has higher priority than -o
    -name PATTERN   Match file name (w/o directory name) to PATTERN
    -iname PATTERN  Case insensitive -name
    -path PATTERN   Match path to PATTERN
    -ipath PATTERN  Case insensitive -path
    -regex PATTERN  Match path to regex PATTERN
    -type X         File type is X (one of: f,d,l,b,c,...)
    -perm MASK      At least one mask bit (+MASK), all bits (-MASK),
                    or exactly MASK bits are set in file's mode
    -mtime DAYS     mtime is greater than (+N), less than (-N),
                    or exactly N days in the past
    -mmin MINS      mtime is greater than (+N), less than (-N),
                    or exactly N minutes in the past
    -newer FILE     mtime is more recent than FILE's
    -inum N         File has inode number N
    -user NAME/ID   File is owned by given user
    -group NAME/ID  File is owned by given group
    -size N[bck]    File size is N (c:bytes,k:kbytes,b:512 bytes(def.))
                    +/-N: file size is bigger/smaller than N
    -links N        Number of links is greater than (+N), less than (-N),
                    or exactly N
    -prune          If current file is directory, don't descend into it
If none of the following actions is specified, -print is assumed
    -print          Print file name
    -print0         Print file name, NUL terminated
    -exec CMD ARG ; Run CMD with all instances of {} replaced by
                    file name. Fails if CMD exits with nonzero
    -delete         Delete current file/directory. Turns on -depth option

【问题讨论】:

  • UNIX 根本不存储文件的创建日期,因此如果您有其他工具在创建文件时记录该信息,则只能获取文件的创建日期/时间。是否还有其他您感兴趣的日期(例如上次修改日期)?
  • 是的,修改日期也很好,应该比创作要好。或上次复制日期或类似日期。

标签: regex parsing shell sed read-write


【解决方案1】:

sed 的pat1,pat2 表示法是基于行的。可以这样想,pat1 为其命令设置启用标志,pat2 禁用该标志。如果pat1 和pat2 都在同一行,则将设置标志,因此在您的情况下,打印包括&lt;user&gt; 行之后的所有内容。有关更多信息,请参阅grymoire's sed howto。

在这种情况下,sed 的替代方法是使用支持环视断言的 grep,例如GNU grep:

find . -type f -name '*.nfo' | xargs grep -oP '(?<=<user>).*(?=</user>)'

如果 grep 不支持-P,可以使用 grep 和 sed 的组合:

find . -type f -name '*.nfo' | xargs grep -o '<user>.*</user>' | sed 's:</\?user>::g'

输出:

./file1.nfo:test1
./file2.nfo:test2

请注意,您应该注意issues involved with passing files on to xargs,并可能改用-exec ...。

【讨论】:

  • 谢谢。但似乎我这里的环境中没有实现功能-P:grep: invalid option -- 'P'
  • 找到 . -type f -name '*.nfo' | xargs grep -o '.*' | sed 's:\?user>::g' ---- 这是有效的。我找回了用户。现在我必须玩弄它。非常感谢
  • 但它只给了我“test1”而不是文件名。我还需要 nfo 文件中的更多部分。但我会在这里玩耍和搜索。我相信我会找到一些他们已经发布的解决方案。谢谢
  • 再次更新。它正在使用路径/文件名和内容。再次感谢
【解决方案2】:

碰巧grep 以您需要的格式输出,对于单行来说就足够了。

默认情况下,grep '' *.nfo 将输出如下内容:

file1.nfo:random data  
file1.nfo:<user>test1</user>  
file1.nfo:some more random data  
file2.nfo:not needed  
file2.nfo:<user>test2</user>  
file2.nfo:etc etc  

通过添加-P 选项(Perl RegEx),您可以将输出限制为仅匹配:

grep -P "<user>\w+<\/user>" *.nfo

输出:

file1.nfo:<user>test1</user>  
file2.nfo:<user>test2</user>  

现在-o 选项(仅显示匹配的内容)可以节省时间,但我们需要更高级的正则表达式,因为不需要标签:

grep -oP "(?<=<user>)\w+(?=<\/user>)" *.nfo > /test/database.txt

cat /test/database.txt 的输出:

file1.nfo:test1 
file2.nfo:test2  

在这里解释正则表达式:http://regex101.com/r/oU2wQ1

你的整个脚本就变成了一个命令。

更新:

如果您没有--perl-regexp 选项,请尝试:

grep -oE "<user>\w+<\/user>" *.nfo|sed 's#</?user>##g' > /test/database.txt

【讨论】:

  • 好的,非常感谢。
  • -P 和 --perl-regexp 是同义词。您可以互换使用它们。
  • \w 是只有某些工具才能理解的缩写。试试 POSIX 等效的 [[:alnum:]_]
【解决方案3】:

你只需要:

find -name '*.nfo' | xargs awk -F'[><]' '{print FILENAME,$3}'

如果您的文件中包含的不仅仅是您在示例输入中显示的内容,那么这可能就是您所需要的:

... awk -F'[><]' '/<user>/{print FILENAME,$3}' file

试试这个(未经测试):

> outfile
find -name '*.nfo' -printf "%p %Tc\n" |
while IFS= read -r fname tstamp
do
      awk -v tstamp="$tstamp" -F'[><]' -v OFS=":::" '
          { a[$2] = a[$2] sep[$2] $3; sep[$2] = ", " }
          END {
              print a["string1"], FILENAME, tstamp, a["string4"], a["string3"], a["hobby"], a["string2"]
          }
      ' "$fname" >> outfile
done

仅当您的文件名不包含空格时,上述内容才有效。如果可以,我们需要调整循环。

如果您的 find 不支持 -printf 的替代方案(建议 - 认真考虑获得现代“find”!):

> outfile
find -name '*.nfo' -print |
while IFS= read -r fname
do
      tstamp=$(stat -c"%x" "$fname")
      awk -v tstamp="$tstamp" -F'[><]' -v OFS=":::" '
          { a[$2] = a[$2] sep[$2] $3; sep[$2] = ", " }
          END {
              print a["string1"], FILENAME, tstamp, a["string4"], a["string3"], a["hobby"], a["string2"]
          }
      ' "$fname" >> outfile
done

如果您没有“stat”,则使用 Google 寻找替代方法以从文件中获取时间戳或考虑解析 ls -l 的输出 - 这是不可靠的,但如果这是您所拥有的一切......

【讨论】:

  • 谢谢,这也很好用。但是文件中还有更多 。我需要选择一些并将其打印到文件中。不过awk好像不错,我去看看文档。
  • 我根据对您的输入文件可能包含的内容的猜测调整了脚本 - 如果您更新问题中的示例输入以更能代表您的真实输入,我将向您展示如何做它在 awk 中。
  • OK,您的编辑现在非常好。我想我现在在我的第一篇文章中发布了我正在寻找的确切内容。但我已经学到了很多关于 find、grep 和现在 awk 的知识:)
  • @Thomas - 你能再试一次吗?您的问题现在完全不清楚和令人困惑。您现在显示一个带有一些标签的文件,然后是带有一堆冒号的其他文件,其中一些文本从标签之间以不可感知的顺序出现,一些 PATH/FILENAME 文本不知从何而来,等等......只需显示一个几个示例输入文件和您希望从中获得的输出,并说明为什么应该是输出。
  • 我发布了一个更新的解决方案。请注意,您无法从 UNIX 获取文件的创建时间,因此我在回答中使用了上次修改时间。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多