How to make find and printf works in bash script

后端 未结 1 1237
失恋的感觉
失恋的感觉 2021-01-29 00:15

My environment: Linux Redhat, Bash 3.5

I have created a bash script to list all of files in sub directories using script below.

#!/bin/bash
for i in $( f         


        
1条回答
  •  夕颜
    夕颜 (楼主)
    2021-01-29 00:29

    Since you don't specify in what way your attempt failed [See note 1, below], I've just provided some thoughts about how to solve the underlying question of producing lists of checksums for each directory.

    Your use of find is both unnecessary and unsafe (if there are any directories whose names include whitespace or shell metacharacters.

    For example,

    $( find . -maxdepth 1 -mindepth 1 -type d )
    

    is roughly the same as the "glob"

    ./*/
    

    except that the glob does not perform pathname expansion or word-splitting on the individual directories. Furthermore, you could use the glob */, which will avoid the ./ prefix, which you evidently don't want.

    Also,

    b=$(echo $i | awk '{print substr($1,3); }' )
    

    could be written much more simply as

    b=${i:3}   # Bash substring notation
    

    or

    b=${i#./}  # Posix standard prefix deletion
    

    But, as noted above, you could use a glob and thereby avoid any having to remove the prefix.

    So you could have simply written:

    #!/bin/bash
    for b in */; do
        find "$b" -type f > "$b/$b.txt"
    done
    

    Note the quotes around the variable expansions. If you really need the %b format to print filenames, then you definitely need to quote the expansions to avoid problems with whitespace and shell metacharacters, but anyway you should develop the a habit of quoting variable expansions.

    That will produce a slightly different output than your script since it doesn't place the ./ at the beginning of each line in the output file, but if you really wanted that you could use find "./$b" ....

    I don't understand why you feel the need to use printf to output the filename for the SHA-1 checksums, because sha1sum already prints the filename. It's true that it does so after the checksum instead of before. There is actually a good reason for that behaviour: since there is no way of knowing what characters the filename might include, putting the checksum first makes it easier to parse the output.

    Note that in the output of sha1sum, the output for each file whose name includes a backslash or newline, the checksum line will start with a backslash and the backslash and newline characters in the filename will be escaped. This produces an effect somewhat similar to that of using the %b format to printf.

    If you really want to turn the order around, you can do so easily enough with awk:

    #!/bin/bash
    for b in */; do
        find "$b" -type f '!' -iname '*thumbs.db*' -exec sha1sum {} + |
        awk '{print "$2:$1"}' > "$b/$b.txt"
    done
    

    Using + instead of ; to terminate the -exec option lets find execute the command with a list of filenames instead of just a single filename, which is much more efficient.


    Notes

    1. See the Stack Overflow help page, from which I quote:

      Questions seeking debugging help ("why isn't this code working?") must include the desired behavior, a specific problem or error and the shortest code necessary to reproduce it in the question itself. Questions without a clear problem statement are not useful to other readers.

      "It doesn't work" is not a "specific problem or error". You should always include the literal text of the error message or a clear statement of how the result of the program failed to meet your expectations.

    0 讨论(0)
提交回复
热议问题