Unexpected output when printing substring

Asked

Viewed 186 times

2

I’m doing a file search in a given directory and printing its respective path.

find ./ -exec sh -c "echo $(dirname "{}")" \;

Output:

./algo/desse/tipo

Since in some cases this path is a little big, I decided to take only the parent directory, in this case the substring algo.

For this, I tried the following commands below:

dirname "{}" | awk -F"/" '{ print $2 }'

dirname "{}" | sed "s/\(\/\.*\/\)/\1/"

dirname "{}" | cut -d'/' -f2

However, none of them returned me the expected result. The awk returns me blank while the sed and the cut returns me literally a point (.).


This is the command I want to run:

find ./ -regex '\./.*_.*\.\(doc\|docx\|md\|xls\)$' -exec sh -c '

    status=$(stat -c "%.10w;%.10y;$(dirname "{}");$(basename "{}")" "{}") 

    if{something}

' \;

For each find result I am already printing a formatted line, the problem is that in the command dirname if I add the pipe | awk -F"/" '{ print $2 }' he doesn’t change my result.

  • What could I be doing wrong??
  • It is something related to the use of single quotes??
  • There’s a better way to get the information I need?
  • 1

    your scripts are confused...show a concrete example...do as I did in my reply: show a listing of directories and files, and the lines you want as a result

2 answers

2

I think with awk can do what you want:

$ find .
.
./bbb
./bbb/bbb.txt
./aaa
./aaa/aaa.txt
./ccc
./ccc/ccc.txt

(pegando so' os arquivos)
$ find . -type f | awk -F/ ' { print $2 }'
bbb
aaa
ccc

$
  • 1

    cool!! Maybe find . -type f | awk -F/ '{ print $(NF-1) }'

1


You can call the awk directly at the exit of find, without needing the exec:

find ./  | awk -F"/" '{print $2}'

Only that find ./ also includes the current directory, which means that it also returns ./ in its results. And by passing this to the awk, it ends up printing a blank line (since in this case there is nothing after the bar).

If you want, you can get around it with a if:

find ./  | awk -F"/" '{if ($2) { print $2 } else { print $1 } }'

Thus, the current directory (which the find returns as ./) will be printed as ., and other directories will be printed normally.


But this solution still generates a lot of repetition. If you have multiple files in the same directory, for example, the name of this directory will be printed several times.

You can eliminate repetitions using sort (to sort the results) and then with uniq (that eliminates consecutive lines that are equal):

find ./  | awk -F"/" '{if ($2) { print $2 } else { print $1 } }' |sort |uniq

Finally, you can use the option -c, which causes the uniq return the amount of occurrences for each line:

find ./  | awk -F"/" '{if ($2) { print $2 } else { print $1 } }' |sort |uniq -c

Just remember that this command will also return the files that are in the current directory. If you only want the directories, use find ./ -type d.


Already for your complete command, I find it easier to make one for in the results of find, then you can execute the commands you want:

for dir in $(find ./ -regex '\./.*_.*\.\(doc\|docx\|md\|xls\)$' | awk -F"/" '{if ($2) { print $2 } else { print $1 } }'|sort|uniq )
do
    status=$(stat -c "%.10w;%.10y;$(dirname "$dir");$(basename "$dir")" "$dir") 

    if{something}
    etc....
done

With each iteration of for, the variable $dir will be the name of one of the directories found. Inside the for it is easier to execute as many commands as you want, without having to put $() within another, join with Pipes, etc. I believe this makes it simpler and clearer.

Although in the example above, how are you using dirname and basename, nor would you need the awk after the find. But anyway, using a for in the results of find allows you to put as many commands as you want, and I find it easier than the way you are trying. The -exec I usually use when it’s to execute a single simple command, for more complex things I prefer the for even.


If you want to use the awk within the for, in accordance with his last comment:

for dir in $(find ./ -regex '\./.*_.*\.\(doc\|docx\|md\|xls\)$' 
do
    status=$(stat -c "%.10w;%.10y;$(dirname $dir | awk -F"/" '{if ($2) { print $2 } else { print $1 } }');$(basename "$dir")" "$dir") 
    echo $status
done

If any of the files/directories have space in the name, just set the variable IFS before the for:

IFS=$'\n'

for dir ...

With that, the for considers line breaks (and no longer space) as separator from your records, treating file names and directories with spaces as if they were one thing.

To restore the original value of the variable, I suggest saving it before the change:

OLDIFS=$IFS
IFS=$'\n'
for dir ....
....

IFS=$OLDIFS
  • hkotsubo, when I encapsulate this command with $() to play your result for a variable, it does not interpret the pipe |... can you say what that can be??

  • @Gabrielhardoim I tested here: resultado=$(find ./ | awk -F"/" '{if ($2) { print $2 } else { print $1 } }' |sort |uniq ) - worked, if you do echo $resultado will see the printed result. Otherwise, you can edit the question and put the command you are trying to run...

  • awk -F"/" '{if ($2) { print $2 } else { print $1 } }' === awk -F/ '{print $(NF)}'

  • @fedorqui This is only true if you have at most two fields. If the directory is ./a/b/c/d/e/f, $NF will get the f, but AP wants the a in that case

  • That’s true. It’s unclear to me exactly what AP is looking for. Directory containing something? Maybe grep -l work better? (Sorry, I am using google Translate)

  • @hkotsubo I edited the question with the command I am using.. it became clearer this way?

  • 1

    @Gabrielhardoim Updated the answer

  • @hkotsubo became a little clearer, but I put the awk after the dirname and not after the find

  • 1

    @Gabrielhardoim I put the awk after the dirname and the result changes yes. I updated the answer

  • @hkotsubo I only have one question... when the find returns a directory with space in the name (/um exemplo/arquivo.txt) the for kind of splits the name.. how do I get around this??

  • 1

    @Gabrielhardoim Updated the answer again :-)

  • 1

    @hkotsubo Thank you very much guy!!

Show 7 more comments

Browser other questions tagged

You are not signed in. Login or sign up in order to post.