【问题标题】:Indexing files and parsing name索引文件和解析名称
【发布时间】:2014-11-07 17:52:41
【问题描述】:

我有一个目录,./grd_files/lat36/,其中有 7 个文件(n36e114.grdn36e115.grdn36e116.grdn36e117.grdn36e118.grdn36e119.grdn36e120.grd。也在下面@987654329 @ 是其他文件夹,命名为lat37lat38lat39。每个文件夹都包含一些文件,其格式与lat36 中的文件格式相同,只是代替n36e114.grd,文件中的e114 经度lat37 文件夹将被称为 n37e114。现在,并非所有 lat** 文件夹都包含所有经度,但我需要它们。

我已经编写了脚本的一部分来确定哪个 lat** 文件夹中的列最多(它是 lat36,有 7 个经度)。我想将lat36 文件夹中存在的经度与其他文件夹进行比较,如果另一个文件夹中缺少一列,我会做的。我可以处理 if then 语句,但我不知道如何比较 bash 中的列表。

我正在考虑列出row1 文件夹中的文件名,并将其与其他文件夹中的文件进行比较,但名称不会也不应该匹配——只有名称的列部分将并且应该匹配。到目前为止,我已经尝试创建一个文件名数组,然后将其解析为名称的列部分。请注意,这些实际上是地图图块,因此名称实际上是北移(行)和缓动(col)坐标格式,例如n36e114.grd。所以我想隔离名称的所有e114 样式部分并检查并确保它们存在于其他行中。我希望这是有道理的。以下是我尝试过的,但我在 bash 语法方面不是很好,所以我很难过。非常感谢帮忙。 col_list_raw=( $(find $maxdirectory -name ".grd" -exec basename {} .grd \;) ) col_list=( for c in ${col_list_raw[@]}; do echo ${col_list_raw[$c]:3:7}; done ) 其中$maxdirectory 是列最多的一个。*


更新:我已经删除了上面斜体中描述的内容,并尝试合并来自 John1024 的解决方案。下面是代码。

cd ./grd_files  
for row in lat*/
    do
        ls "$row"  | sed 's/.*lon/lon/' >"${row%/}.tmp"
done
for f in lat*.tmp
    do
        grep -vFf "$f" ${latXX}.tmp >missing.tmp
        [ -s missing.tmp ] && echo ${f%.tmp} is missing $(cat missing.tmp)
done
cd ..

其中latXX 是经度最多的文件夹。 John1024 的第一个循环运行良好,我得到了每个 lat** 文件夹的正确列表,但第二个循环直接比较列表,返回:

lat37 is missing n36e114.grd n36e115.grd n36e116.grd n36e117.grd n36e118.grd n36e119.grd n36e120.grd
lat38 is missing n36e114.grd n36e115.grd n36e116.grd n36e117.grd n36e118.grd n36e119.grd n36e120.grd
lat39 is missing n36e114.grd n36e115.grd n36e116.grd n36e117.grd n36e118.grd n36e119.grd n36e120.grd

我需要该循环来仅比较文件名的部分。即我想检查每个文件夹是否存在每个经度。因此,如果文件 `n37e114.grd' 存在,则不会发生任何事情,但如果它不存在,则返回该信息,我可以根据丢失的文件执行命令。我希望我的编辑能够清除命名约定并且是可以理解的。再次感谢您的帮助。上午


解决方案:

感谢@John1024 的帮助,我找到了解决方案。我已经复制了下面的最终解决方案。在此之后,我读入 *.out 文件并对它们的每一行执行我的命令。

cd ./grd_files  
for lat in */
    do
    ls "$lat" | sed 's/[a-z][1-9][1-9].*\([a-z][0-9][0-9]*\).grd/\1/' >"${lat%/}.tmp"
done
for file in *.tmp
    do
    lat=$(echo $file | awk -F "." '{print $1}')
    grep -vFf "$file" ${xXX}.tmp >${lat}missing.out
    [ -s ${lat}missing.out ] && echo ${file%.tmp} is missing $(cat ${lat}missing.out)
done

【问题讨论】:

  • 数据示例以及价值 1000 字的想要输出...

标签: bash shell filenames


【解决方案1】:

正如我在评论中告诉你的,提供测试数据是一种很好的做法。在这种情况下,如果提供一个创建测试用例的脚本,你会得到更多的答案,比如下一个:

mkdir grid
cd grid
mkdir lat3{5..9}
    #if you don't know the {3..9} expansion, simply write
    #mkdir lat36 lat37 lat38 lat39
touch lat35/n35e111.grd
touch lat36/n36e11{4..9}.grd lat36/n36e120.grd
touch lat37/n37e11{4,6,8}.grd
touch lat38/n38e11{4..9}.grd
#39 missing all files

创建测试用例的脚本比整页单词更有帮助。 ;) 或者,如果没有脚本,至少提供find 的输出,如find grid -print。你的第一次编辑有点帮助,(我错过了)和对@John1024 的工作 +100。

现在谈谈解决方案。

您的最终解决方案有一个问题。如果具有 MOST LONGITUDES 的目录(您的 latXX)缺少一些其他目录中存在的网格文件怎么办?例如。它拥有最多的网格文件,但仍然不是全部。就像上面的测试用例一样,lat36 包含 7 个文件(大部分),但仍然缺少一个文件 n36e111.grd(因为 111 仅存在于 lat35 中)?

因此我创建了一个替代解决方案,它消除了这个问题并将结果显示为下一个矩阵:

    111 114 115 116 117 118 119 120
35: +   no  no  no  no  no  no  no      # the 111 is here
36: no  +   +   +   +   +   +   +       # the dir with a MOST of longitudes but missing 111
37: no  +   no  +   no  +   no  no
38: no  +   +   +   +   +   +   no
39: no  no  no  no  no  no  no  no      # missing all longitudes

脚本

start="./test/grid"
cd "$start" || err "can cd to $start" || exit 1

known_longs=$(find . -type f -name \*.grd -print | sed 's:.*/n.*e\([0-9][0-9]*\)\.grd:\1:' | sort -u)
known_lats=$(find . -type d -print | grep -oP 'lat\K\d+(?=/?)' | sort -u)

print_matrix() {
    echo -ne "\t"
    paste -s - <<<"$known_longs"
    for lat in $known_lats
    do
        echo -en "$lat:"
        for long in $known_longs
        do
            [[ -e "./lat${lat}/n${lat}e${long}.grd" ]] && echo -en "\t+" || echo -en "\tno"
        done
        echo
    done
}
print_matrix

逻辑很简单:

  • 搜索所有已知长,例如对于包含eNNN 的文件名
  • 搜索所有 已知 lats,例如对于 latNN 的目录
  • 在一个循环中测试文件是否存在

上面打印的矩阵可能不是很有用,因为您可能想对找到或丢失的文件做一些事情,所以这里是脚本的 action 变体。

start="./test/grid"
cd "$start" || err "can cd to $start" || exit 1

known_longs=$(find . -type f -name \*.grd -print | sed 's:.*/n.*e\([0-9][0-9]*\)\.grd:\1:' | sort -u)
known_lats=$(find . -type d -print | grep -oP 'lat\K\d+(?=/?)' | sort -u)

do_if_exists() {
    local xlat="$1"
    local xlong="$2"
    filename="n${xlat}e${xlong}.grd"
    #do nothing
}

do_if_missing() {
    local xlat="$1"
    local xlong="$2"
    filename="n${xlat}e${xlong}.grd"
    echo "from lat$xlat missing $filename"
}

do_actions() {
    for lat in $known_lats
    do
        for long in $known_longs
        do
            [[ -e "./lat${lat}/n${lat}e${long}.grd" ]] && do_if_exists $lat $long || do_if_missing $lat $long
        done
    done
}
do_actions

对丢失的文件执行什么操作(回显丢失的内容),输出是下一个:

from lat35 missing n35e114.grd
from lat35 missing n35e115.grd
from lat35 missing n35e116.grd
from lat35 missing n35e117.grd
from lat35 missing n35e118.grd
from lat35 missing n35e119.grd
from lat35 missing n35e120.grd
from lat36 missing n36e111.grd
from lat37 missing n37e111.grd
from lat37 missing n37e115.grd
from lat37 missing n37e117.grd
from lat37 missing n37e119.grd
from lat37 missing n37e120.grd
from lat38 missing n38e111.grd
from lat38 missing n38e120.grd
from lat39 missing n39e111.grd
from lat39 missing n39e114.grd
from lat39 missing n39e115.grd
from lat39 missing n39e116.grd
from lat39 missing n39e117.grd
from lat39 missing n39e118.grd
from lat39 missing n39e119.grd
from lat39 missing n39e120.grd

当然可以优化更多,比如:

  • 只查找一次(如果目录树很大,则有帮助 - 通过find command 创建文件名列表
  • 不要测试每个文件,而是测试之前创建的文件名列表中的文件名是否存在

喜欢下一个

startdir="./test/grid"
(cd "$startdir" || err "can cd to $start" || exit 1

gridlist="/tmp/griglist.$$"
trap "rm -f $gridlist;exit" 0 2

find . -regex '\./lat[0-9][0-9]*.*' -print >$gridlist
known_longs=($(sed -n 's:^.*/n[0-9][0-9]*e\([0-9][0-9]*\)\.grd$:\1:p' $gridlist | sort -u))
known_lats=($(grep -oP '/lat\K\d+((?=/?)|$)' $gridlist | sort -u))

full_list() {
    for lat in ${known_lats[@]}
    do
        for long in ${known_longs[@]}
        do
            echo "./lat${lat}/n${lat}e${long}.grd"
        done
    done
}
comm -13 $gridlist <(full_list)) | while read missing
do
    #do something with the miising file
    echo "$missing"
done

【讨论】:

    【解决方案2】:

    该问题包括两种不同的文件命名方案。两者的工作方式相同,但为了保持简单直观,此答案使用第一种方案。

    可以循环遍历 bash 数组以查找丢失的列。但是,grep 非常适合这项任务,大大简化了逻辑,而且,如果有很多列和行,它可能会快得多。使用grep

    cd ./grd_files
    for row in row*/
    do
        ls "$row"  | sed 's/.*col/col/' >"${row%/}.tmp"
    done
    for f in row*.tmp
    do
        grep -vFf "$f" row1.tmp >missing.tmp
        [ -s missing.tmp ] && echo ${f%.tmp} is missing $(cat missing.tmp)
    done
    

    上面的第一个循环,创建存在于每一行中的列列表。这些列表保存在名为 row1.tmp、row2.tmp 等的临时文件中。

    第二个循环将这些列表中的每一个与参考行 row1.tmp 进行比较。该行中缺少的列列表保存在临时文件missing.tmp 中。如果missing.tmp 具有非零大小,则缺少列并生成报告。

    为了清理,可能需要删除 tmp 文件。如果是这样,请将此行添加到脚本的末尾:

    rm row*.tmp missing.tmp
    

    更高级的版本

    使用进程替换,可以消除对许多临时文件的需求:

    trap "rm missing.tmp" EXIT
    for row in row*/
    do
        ls row1/  | sed 's/.*col/col/' | grep -vFf <(ls "$row"  | sed 's/.*col/col/') >missing.tmp
        [ -s missing.tmp ] && echo $row is missing $(cat missing.tmp)
    done
    

    此版本还使用trap 来确保在脚本完成后删除唯一剩余的临时文件。

    根据修改后的问题使用其他命名方案

    cd ./grd_files  
    for row in lat*/
        do
            ls "$row"  | sed 's/.*n[0-9][0-9]e/e/' >"${row%/}.tmp"
    done
    for f in lat*.tmp
        do
            grep -vFf "$f" ${latXX}.tmp >missing.tmp
            [ -s missing.tmp ] && echo ${f%.tmp} is missing $(cat missing.tmp)
    done
    cd ..
    

    【讨论】:

    • @amoodie 我明白了。使用新的命名方案,sed 命令没有发挥作用:它应该删除所有符合经度规范的内容。查看更新的答案,然后检查纬度是否已从您的 tmp 文件中删除。
    猜你喜欢
    • 2016-03-05
    • 2012-08-01
    • 2015-11-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-07
    • 2013-12-17
    • 1970-01-01
    相关资源
    最近更新 更多