bash来识别和验证文件头

bash来识别和验证文件头,bash,Bash,使用下面以制表符分隔的文件,我试图验证标题行1,然后将该数字存储在变量$header中,以便在几个if语句中使用。如果$header等于10,则文件具有预期的字段数,但如果$header小于10,则文件缺少:的标题,缺少的标题字段将打印在下面。bash似乎很接近,如果我单独使用awk,它似乎可以完美地工作,但我似乎无法在if中使用它。谢谢 file.txt file2.txt 猛击 file.txt的所需输出 file1.txt的所需输出 这段代码将完全满足您的要求。让我知道它是否适合你 fo

使用下面以制表符分隔的文件,我试图验证标题行1,然后将该数字存储在变量$header中,以便在几个if语句中使用。如果$header等于10,则文件具有预期的字段数,但如果$header小于10,则文件缺少:的标题,缺少的标题字段将打印在下面。bash似乎很接近,如果我单独使用awk,它似乎可以完美地工作,但我似乎无法在if中使用它。谢谢

file.txt

file2.txt

猛击

file.txt的所需输出

file1.txt的所需输出


这段代码将完全满足您的要求。让我知道它是否适合你

 for f in ./*.txt; do

      [[ $( head -1 $f | awk '{ print NF}' ) -eq 10 ]]  && echo "File $f has all the fields on its header" || echo "File $f is missing " $( echo "Index   Chr Start   End Ref Alt Freq    Qual    Score   Input $( head -1 $f )" | tr ' ' '\n' | sort | uniq -c |  awk '/1 / {print $2}' ); 
 done
输出:

File ./file2.txt is missing  Input
File ./file.txt has all the fields on its header

这段代码将完全满足您的要求。让我知道它是否适合你

 for f in ./*.txt; do

      [[ $( head -1 $f | awk '{ print NF}' ) -eq 10 ]]  && echo "File $f has all the fields on its header" || echo "File $f is missing " $( echo "Index   Chr Start   End Ref Alt Freq    Qual    Score   Input $( head -1 $f )" | tr ' ' '\n' | sort | uniq -c |  awk '/1 / {print $2}' ); 
 done
输出:

File ./file2.txt is missing  Input
File ./file.txt has all the fields on its header
下面是GNU awk下一个文件中的一个:

脚本处理的第一个文件在a中定义了以下文件应该具有的头,并将它们与b中的头进行比较。

这里是GNU awk nextfile中的头:

脚本处理的第一个文件在a中定义了以下文件应具有的标题,并在b中将其与之进行比较。

如果愿意,可以使用awk,但bash本身完全能够处理第一行字段比较。如果维护预期字段名的数组,则可以轻松地将第一行拆分为字段,与预期的字段数进行比较,如果从任何给定文件中读取的字段数少于预期的字段数,则输出缺少的字段的标识

下面是一个以文件名为参数的简短示例。您需要从stdin获取大量文件的文件名,或者根据需要使用xargs。脚本只需读取每个文件中的第一行,将行分隔为字段,检查字段计数,并在短错误消息中输出任何缺少的字段:

#!/bin/bash

declare -i header=10    ## header has 10 fields
## aray of field names (can be read from 1st file)
fields=( "Index"
         "Chr"
         "Start"
         "End"
         "Ref"
         "Alt"
         "Freq"
         "Qual"
         "Score"
         "Input" )

for i in "$@"; do           ## for each file given as argument
    read -r line < "$i"     ## read first line from file into 'line'

    oldIFS="$IFS"           ## save current Internal Field Separator (IFS)
    IFS=$'\t'               ## set IFS to word-split on '\t'

    fldarray=( $line );     ## fill 'fldarray' with fields in line

    IFS="$oldIFS"           ## restore original IFS

    nfields=${#fldarray[@]} ## get number of fields in 'line'

    if (( nfields < header ))   ## test against header
    then
        printf "error: only '%d' fields in file '%s'\nmissing:" "$nfields" "$i"
        for j in "${fields[@]}" ## for each expected field
        do  ## check against those in line, if not present print
            [[ $line =~ $j ]] || printf " %s" "$j"
        done
        printf "\n\n"   ## tidy up with newlines
    fi
done
示例使用/输出

仔细看,虽然awk可以做许多bash自己无法做的事情,但bash完全可以解析文本。

如果您愿意,可以使用awk,但bash完全可以自己处理第一行字段比较。如果维护预期字段名的数组,则可以轻松地将第一行拆分为字段,与预期的字段数进行比较,如果从任何给定文件中读取的字段数少于预期的字段数,则输出缺少的字段的标识

下面是一个以文件名为参数的简短示例。您需要从stdin获取大量文件的文件名,或者根据需要使用xargs。脚本只需读取每个文件中的第一行,将行分隔为字段,检查字段计数,并在短错误消息中输出任何缺少的字段:

#!/bin/bash

declare -i header=10    ## header has 10 fields
## aray of field names (can be read from 1st file)
fields=( "Index"
         "Chr"
         "Start"
         "End"
         "Ref"
         "Alt"
         "Freq"
         "Qual"
         "Score"
         "Input" )

for i in "$@"; do           ## for each file given as argument
    read -r line < "$i"     ## read first line from file into 'line'

    oldIFS="$IFS"           ## save current Internal Field Separator (IFS)
    IFS=$'\t'               ## set IFS to word-split on '\t'

    fldarray=( $line );     ## fill 'fldarray' with fields in line

    IFS="$oldIFS"           ## restore original IFS

    nfields=${#fldarray[@]} ## get number of fields in 'line'

    if (( nfields < header ))   ## test against header
    then
        printf "error: only '%d' fields in file '%s'\nmissing:" "$nfields" "$i"
        for j in "${fields[@]}" ## for each expected field
        do  ## check against those in line, if not present print
            [[ $line =~ $j ]] || printf " %s" "$j"
        done
        printf "\n\n"   ## tidy up with newlines
    fi
done
示例使用/输出


综上所述,虽然awk可以做许多bash自己做不到的事情,但bash完全能够解析文本。

10是固定的,或者应该根据标题记录的不同而变化。10是固定的,因为这是所需的标题字段数量。如果NF小于该值,则打印缺少的NF。谢谢:。如果一个文件有11列或更多列怎么办?这有多大问题?到目前为止,您已经表达了对9个或更少列的担忧,但可能太多。10是固定的还是应该根据标题记录而变化10是固定的,因为这是所需标题字段的数量。如果NF小于该值,则打印缺少的NF。谢谢:。如果一个文件有11列或更多列怎么办?这有多大问题?到目前为止,您已经表达了对9列或更少列的关注,但可能太多了。我不禁认为,“一行”最好显示为if、then和else,fi分布在多行上。我对该行末尾的脚本有保留意见,但它不可读,因为它太长,一行都看不懂。请明智地使用换行符——并且比现在更加丰富。从grep到awk的管道传输也是一种代码气味——因为这意味着一个人没有充分利用awk,因为它可以轻松完成grep的工作:awk'/1/{print$2}。这里也有大量的遗漏引语,这会引起注意。@JonathanLeffler是的,我知道。我不想表现得很整洁。效率很高。“你说得对,查尔斯,格雷普到奥克的那一次真是太差劲了。”。我现在就编辑它。谢谢我不认为通过将代码全部格式化为一行来解决代码中的计算机效率问题。从人类效率的角度来看,多行编码使得阅读变得困难。当然,这只是我的观点,但是……当你看到代码时,发现grep | awk类型的低效性就更容易了。我忍不住认为“一行”最好显示为if、then和else,fi分布在多行上。我有预订
关于那一行最远端的脚本,我不太清楚,但它是不可读的,因为它对于一行来说太长了。请明智地使用换行符——并且比现在更加丰富。从grep到awk的管道传输也是一种代码气味——因为这意味着一个人没有充分利用awk,因为它可以轻松完成grep的工作:awk'/1/{print$2}。这里也有大量的遗漏引语,这会引起注意。@JonathanLeffler是的,我知道。我不想表现得很整洁。效率很高。“你说得对,查尔斯,格雷普到奥克的那一次真是太差劲了。”。我现在就编辑它。谢谢我不认为通过将代码全部格式化为一行来解决代码中的计算机效率问题。从人类效率的角度来看,多行编码使得阅读变得困难。当然,这只是我的意见,但是……当你看到代码时,发现grep | awk类型的低效更容易。感谢所有伟大的解决方案,它们都工作完美:。感谢所有伟大的解决方案,它们都工作完美:。
File ./file2.txt is missing  Input
File ./file.txt has all the fields on its header
$ awk '
FNR==NR {
    for(n=1;n<=NF;n++)
        a[$n]
    nextfile
}
NF==(n-1) {
    print FILENAME " file has expected number of fields"
    nextfile
}
{
    for(i=1;i<=NF;i++)
        b[$i]
    print FILENAME " is missing header for: " 
    for(i in a)
    if(i in b==0)
        print i
    nextfile
}' file1 file1 file2
file1 file has expected number of fields
file2 is missing header for: 
Input
#!/bin/bash

declare -i header=10    ## header has 10 fields
## aray of field names (can be read from 1st file)
fields=( "Index"
         "Chr"
         "Start"
         "End"
         "Ref"
         "Alt"
         "Freq"
         "Qual"
         "Score"
         "Input" )

for i in "$@"; do           ## for each file given as argument
    read -r line < "$i"     ## read first line from file into 'line'

    oldIFS="$IFS"           ## save current Internal Field Separator (IFS)
    IFS=$'\t'               ## set IFS to word-split on '\t'

    fldarray=( $line );     ## fill 'fldarray' with fields in line

    IFS="$oldIFS"           ## restore original IFS

    nfields=${#fldarray[@]} ## get number of fields in 'line'

    if (( nfields < header ))   ## test against header
    then
        printf "error: only '%d' fields in file '%s'\nmissing:" "$nfields" "$i"
        for j in "${fields[@]}" ## for each expected field
        do  ## check against those in line, if not present print
            [[ $line =~ $j ]] || printf " %s" "$j"
        done
        printf "\n\n"   ## tidy up with newlines
    fi
done
$ cat dat/hdr.txt
Index   Chr     Start   End     Ref     Alt     Freq    Qual    Score   Input
1       1       1       100     C       -       1       GOOD    10      .
2       2       20      200     A       C       .002    STRAND BIAS     2       .
3       2       270     400     -       GG      .036    GOOD    6       .

$ cat dat/hdr2.txt
Index   Chr     Start   End     Ref     Alt     Freq    Qual    Score
1       1       1       100     C       -       1       GOOD    10
2       2       20      200     A       C       .002    STRAND BIAS     2
3       2       270     400     -       GG      .036    GOOD    6

$ cat dat/hdr3.txt
Index   Chr     Start   End     Alt     Freq    Qual    Score   Input
1       1       1       100     -       1       GOOD    10      .
2       2       20      200     C       .002    STRAND BIAS     2       .
3       2       270     400     GG      .036    GOOD    6       .
$ bash hdrfields.sh dat/hdr.txt dat/hdr2.txt dat/hdr3.txt
error: only '9' fields in file 'dat/hdr2.txt'
missing: Input

error: only '9' fields in file 'dat/hdr3.txt'
missing: Ref