0

Both of the following lines do return the list of dirs of one of my directories.

The first of them use a name pattern whereas I need to choose dirs given they really are directories to then process them in a loop. I thus would like to use the second:

ls -l  | awk '{print $9}' | grep 'dirsPrefix*'

find . -maxdepth 1 -type d | grep -v \\.$ | awk -F'./' '{print $2}'

But within a ksh for loop, the first is working unlike the second ... Why ? :

(workaround:

for dir in `find . -maxdepth 1 -type d -exec echo "{}" \; | awk -F'/' {print $2}`
do 
  echo "dir: $dir"
done

will work)

This also works:

for dir in `ls -l  | awk '{print $9}' | grep 'dirsPrefix*'`
do 
  echo "dir: $dir"
done

But this does not:

for dir in `find . -maxdepth 1 -type d | grep -v \\.$ | awk -F'./' '{print $2}'`
do 
  echo "dir: $dir"
done
  • found a workaroun using: for dir in `find . -maxdepth 1 -type d -exec echo "{}" \; | awk -F'/' '{print $2}' But still willing to understand why find grep version is not working – user1767316 Aug 9 '17 at 12:31
4

You're trying way too hard to get the directories in the current directory. All you need to do is:

for dir in */; do echo "dir: $dir"; done
# .........^^

If you want to store them in an array:

dirs=( */ )
  • Nice. I didn't know you can make an array like that. Couldn't searching for */ potentially cause a similar problem as parsing ls though if a filename happens to contain a /? – Jesse_b Aug 9 '17 at 13:49
  • Eh I guess you can't put the SOLIDUS slash in a filename anyway. – Jesse_b Aug 9 '17 at 13:56
1

Change it to this:

for dir in $(find . -maxdepth 1 -type d | grep -v \\.$ | awk -F'./' '{print $2}')
do 
  echo "dir: $dir"
done

I can't explain why it works but I know you should pretty much always use $() over backticks.

  • thanks, backticks implementation seems strange then. I prefer it over $() because I tought it was working whith most shells .... But $() must be here for a reason :-) – user1767316 Aug 9 '17 at 12:45
  • 1
    Incorrect. $() is the POSIX standard so it would be the most widely accepted. It also supports nesting. – Jesse_b Aug 9 '17 at 12:46
  • ok I'll try POSIX's $() with #!/bin/sh then ;-) – user1767316 Aug 9 '17 at 12:49
  • 1
    ksh confirms to POSIX and therefore it supports the $() POSIX standard. – Jesse_b Aug 9 '17 at 13:00
1

You need to realize that the command that you are running, viz.,

for dir in `find . -maxdepth 1 -type d | grep -v \\.$ | awk -F'./' '{print $2}'`

there are 2 levels of quote interpretation. In the first level, that is at the level of the command line where from the command is issued, the text under the backquotes `....` is first scanned for backslashes and $variables, just as in a double quotes interpolation. Since that $ is sitting lonesome at the end, so it won't be expanded. There's no harm in escaping it though.

So this shall leave that grep -v \\.$ ----> grep -v \.$

Now after this step is over, it shall be handed over to a subshell for running. And in that subshell the command grep -v \.$ is parsed and the backslash is taken away so what grep executable gets at the end of this phase is: grep -v .$

Now all the nonempty lines will match this pattern and find generated all nonempty lines, for the simple reason that filenames mustn't be nonempty.

So that means all find's output are gonna be selected and hence the inverse of that implies that no output from find shall be selected. So awk is all dressed up but has really nowhere to go.

Solution:

There are many things you can take up to fix the issue.

  • Add one more backslash to the grep, making it : grep -v \\\.$ Based on what happens in each step of quote expansion for this scenario, convince yourself that this fix works.
  • Use the $() form of command interpolation as in this format the quoting begins anew withing the $() unlike the `` form of backquoting.
  • Use single quotes in the grep, making it: grep -v '\.$'
  • Use character classes to escape the . : grep -v '[.]$'
  • Do everything in find command itself:

find . -maxdepth 1 -type d ! -name . -exec sh -c '
   for d do printf "dir: %s\n" "${d:2}"; done
' sh {} +
0

There is a problem with your grep-expression. It will match all directory names ending with ., but it also seems to skew the output somehow to make for confused. There are many things you can do about this.

  • Changing the grep expression to grep -v "^\.$" will probably be more correct (as it will match only the . directory), and it will work in your for-loop

  • As as already pointed out by @Jesse_b, it is better, for many reasons, to use the $(..) expression for execution. This will solve your immediate problem, but not fix the potential bug.

  • You can use find all the way, making this script a bit simpler:

    for dir in `find . -maxdepth 1 -type d ! -name "." -printf "%f "`
    do
       echo $dir
    done
    

Not that all of these solutions will likely be trouble if there are directories with spaces () in their names.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.