【问题标题】:How to move files where the first line contains a string?如何移动第一行包含字符串的文件?
【发布时间】:2019-07-28 05:00:06
【问题描述】:

我目前正在使用以下命令:

grep -l -Z -E '.*?FindMyRegex' /home/user/folder/*.csv | xargs -0 -I{} mv {} /home/destination/folder

这很好用。问题是它在整个文件上使用了grep

我只想在文件的第一行使用grep 命令。

一开始我尝试使用head -1 file |,但没有成功。

【问题讨论】:

    标签: linux bash centos redhat


    【解决方案1】:

    您可以尝试sed '1q' file.csv | grep ... 仅在第一行搜索正则表达式。

    【讨论】:

    • mv: cannot stat 'standard input' no such file or directory (我将文件部分从 grep 移到 sed)
    • 如果你还在使用grep -l,那么输出将是(standard input),这将不适用于mv。下面给出的 awk 答案可能更适合您的情况。
    【解决方案2】:
    gawk 'FNR==1{if($0~/PATTERN/)
        printf "mv %s %s\n",FILENAME, "/target";nextfile}' /path/*.csv
    
    • 首先,在您的正则表达式中:.*?FindMyRegex .*? 没有任何意义,它们可以被删除。
    • 上面的 awk (gawk) 单行代码将为您构建 mv file target 命令行。您可以检查它们,如果您对它们感到满意,请将输出传递到 |sh ,命令将被执行。

    • 用你的正则表达式模式替换PATTERN,用真正的目标目录替换/target

    • 单行假设文件名不包含特殊字符(即空格),如果是这种情况,请将"s 添加到mv cmd。

      李>

    【讨论】:

    • @Socowi 我的意思是.*?Pattern 没有意义。不管贪不贪。
    • 啊,现在我明白你的意思了:grep 在整行中查找匹配项,而不仅仅是从行首开始的匹配项。这里我们对匹配部分不感兴趣,只对是/否答案感兴趣,因此.*patternpattern 相同。如果grep -E 理解非贪婪正则表达式.*?pattern,它总是匹配pattern,因为.*? 前面没有任何内容。
    • @Socowi 是的,除了-E or -P 支持非贪婪之外,他的模式中的.*?.* 没有意义。
    • 可能想在 printf 语句输出中引用文件名。引用会变得混乱。
    【解决方案3】:

    我要为您的脚本添加的更改是 -

         for file in *.csv; do 
            head -1 "$file" | grep -l -Z -E '.*?FindMyRegex' | xargs -0 -I{} mv {} /home/destination/folder; 
         done
    

    【讨论】:

    • 只使用head -1 file | ... 而不是echo $(head ...) | ... 不仅更短、更高效,而且更安全。现在,由于缺少引号,您可能会破坏第一行。另外我认为grep -l 在这里不起作用,因为输入来自管道。您可能想使用grep -q ... && mv "$file" ...
    • 您好,谢谢,我收到 2 个错误:1 个文件名中包含空格。 2 用于 grep 匹配时:mv: cannot stat '(standard input)'...
    【解决方案4】:

    您不需要grepfind,只要您的文件没有嵌入换行符。
    我不知道有什么简单的方法可以让sed 用空值分隔。

    mv $( for f in  /home/user/folder/*.csv;
          do sed -ns '1 { /yourPattern/F; q; }' $f;
          done ) /home/destination/folder/
    

    编辑

    用循环重写。这将运行一个单独的sed 实例来检查每个文件,但至少它不应超过第一行。如果没有命中,它在语法上失败。

    可能需要-E,具体取决于您的正则表达式。

    -n 表示不要从文件中打印记录。
    -s 表示将每个文件视为不同的输入 - 这样文件名并不总是第一个。

    确实需要 GNU sed 用于 F

    【讨论】:

    • 命令中的sed 将读取每个文件中的所有行。即使它只在第一行做了一些逻辑。
    • 是的。我有一个2q,但它正在退出第一个文件的整个过程。 希望它有一个简单的优化器,可以识别只有一行要读取并且没有输出要生成,但我不会假设。这是awk 有点发光的情况。 :)
    【解决方案5】:

    使用 GNU awk 查找文件名,将文件名通过管道传输到 xargs

    gawk -v pattern="myRegex" '
        FNR == 1 {if ($0 ~ pattern) printf "%s\0", FILENAME; nextfile}
    ' *.csv | xargs -0 echo mv -t destination
    

    如果看起来没问题,删除“echo”

    【讨论】:

    • 嗨,我在通配符之前指定了 csv 的路径。得到一个错误 mv:缺少文件操作数
    • 只运行 gawk 命令而不通过管道连接到 xargs:是否打印了任何文件名?
    【解决方案6】:

    试试这个Shellcheck-clean Bash 代码:

    #! /bin/bash
    
    shopt -s nullglob   # Globs that match nothing expand to nothing
    shopt -s dotglob    # Globs match files whose names start with '.'
    
    dest=/home/destination/folder
    
    for file in *.csv ; do
        head -n 1 -- "$file" | grep -qE '.*?FindMyRegex' && mv -- "$file" "$dest"
    done
    
    • 如果目录中没有 .csv 文件,shopt -s nullglob 可防止出错。
    • shopt -s dotglob 确保名称以“.”开头的文件被处理。
    • headmv 选项中的-- 可确保正确处理名称以- 开头的文件。
    • "$file""$dest" 中的引号确保正确处理包含空格(实际上是 $IFS)字符(包括换行符)或全局元字符的名称。

    请注意,正则表达式中的.*? 可能是多余的,并且可能不会像您认为的那样做(grep -E 不会进行非贪婪匹配)。

    【讨论】:

      猜你喜欢
      • 2021-06-09
      • 2019-10-13
      • 1970-01-01
      • 2014-10-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多