1

I had a question that was answered here: https://unix.stackexchange.com/questions/264925/awk-fs-with-back-slashes

After that I tried adding it to a .sh script that starts with:

#!/bin/bash

My Line:

category1Filenames=(\`find . -maxdepth 1 -type f |
gawk 'BEGIN { FS = "(\\\\./)|(\\\\.)" } ; { print $2 }' | sort -u\`)

And then I print the output using:

for genericFilename in ${category1Filenames[@]}; do  
  printf "%s\n" $genericFilename  
done 

But this gives me the error:

gawk: cmd. line:1: warning: escape sequence `\.' treated as plain `.'

If I change the line to be:

category1Filenames=$(find . -maxdepth 1 -type f |
gawk 'BEGIN { FS = "(\\\\./)|(\\\\.)" } ; { print $2 }' | sort -u)

Then it works as expected.

I'm assuming that the problem was with using the back tick characters, but I don't understand why. Probably because I don't understand the behavior of ` vs ' vs ". If anyone can point me to documentation that can explain this behavior (maybe tldp) then it would be greatly appreciated.

4
  • The tldp bash guide is outdated, and in some cases just plain wrong. Commented Feb 22, 2016 at 22:14
  • What is that call to gawk supposed to do? It would be much simpler to just iterate for genericFilename in *; do [[ -f $genericFilename ]] || continue; ...; done. Commented Feb 22, 2016 at 22:18
  • @chepner: gawk is being used to so that I can get to the main filename - separating/stripping the leading ./ and file extensions periods. Example: the find command gives me: ./myfilename.0.log. I only want myfilename. Commented Feb 22, 2016 at 22:48
  • If my answer to your question was too obvious and your question was really 'to what end' then I'll be happy to describe further. I'm not sure I've seen a for loop used in the way you have it shown so I'd be interested (and can open a separate question) in understanding if there is a better approach. Commented Feb 23, 2016 at 3:52

2 Answers 2

1

The backquote (`) is used in the old-style command substitution, The foo=$(command) syntax is recommended instead. Backslash handling inside $() is less surprising, and $() is easier to nest. See BashFAQ82

Using backquotes, you'll have to escape required sequence for gawk:

category1Filenames=(`find . -maxdepth 1 -type f | gawk 'BEGIN { FS = "(\\\./)|(\\\.)" } ; { print $2 }' | sort -u`)

Aside: Feed categoryFilenames array in a loop instead:

while read -r line; do
    categoryFilenames+=("$line")
done < <(find . -maxdepth 1 -type f | gawk 'BEGIN { FS = "(\./)|(\.)" } ; { print $2 }' | sort -u)

Now it is safer to iterate over categoryFilenames like you did, but with double-quotes:

for genericFilename in "${category1Filenames[@]}"; do  
  printf "%s\n" "$genericFilename"  
done  

Don't forget to "Double quote" every literal that contains spaces/metacharacters and every expansion: "$var", "$(command "$var")", "${array[@]}", "a & b".

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks for your answer. Do you say that the while approach is safer because it is bypassing subshells caused by pipelines (taken from mywiki.wooledge.org/ProcessSubstitution)? I'm relatively new to messing around with stdin, Process Substitution, etc... so I'm just trying to understand exactly what I would be doing with your while loop.
You're welcome! No. I suggest you to read Arrays, as it explains it along with several examples. Please also notice @chepner's comment on your question. You might be doing something useless and maybe simply wrong.
Thanks again. Interesting read and from that link i was able to find that it's good to use read with -d '', find with -print0, and understand why you suggest using quotation marks around variables. Without using -d '' and -print0 it will not properly handle filenames with spaces in them. I don't expect any filenames in the group of files I'm focusing on. But it's nice to know and guard against it before hand. I had missed @chepner's comment and hope I answered his question as I'm always eager to learn and find more efficient/appropriate ways of solving a problem.
1

Instead of find, just iterate over all the files in the current directory, and use the shell to process only regular files and extract the file name.

for name in *; do
    [[ -f $name ]] || continue
    name_only=${name%%.*}
    printf "%s\n" "$name_only"
done

1 Comment

thanks! very interesting.. i don't understand how it does what it does but that's what these forums and google are for :). do you feel that this method is any more efficient than the find (if so, why - is it because it doesn't leave the 'shell' whereas using find does)?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.