1

I have to find files with selected permissions and list them as well as their number. Therefore I would like to pipe result of find command to shell and to the next command, which output I want to store in a variable so I could display it nicely later. I would like to have something like

for i in "$@"
do
    find $filename -perm $i | tee /dev/tty | var=${wc -l}
    echo "number of files with $i permission: $var"
done

but var=${wc -l} part doesn't work. Please help.

EDIT I'm aware that I can put entire output of the command to a variable like

var=$(find $filename -perm $i | tee /dev/tty | wc -l)

but then I need only the result of wc -l. How would I get this number from that variable? Would it be possible to display it in reversed order, number first and then the list?

3
  • 2
    FYI, in "$@" is actually the default thing for a for loop to iterate over, so you could literally just write for i; do Commented Mar 6, 2017 at 22:24
  • 1
    ...err, waitaminute. "Number first and then the list"? Huh? The filename list isn't in var at all -- neither stderr nor TTY contents are captured. That list was printed directly to the TTY by tee while the pipeline was running; it has nothing to do with var's contents. Commented Mar 7, 2017 at 12:57
  • 1
    BTW, I've updated my answer to actually do what your title says it wants, vs what your code sample says it wants -- /dev/tty and stdout are two very different things. Commented Mar 7, 2017 at 15:08

1 Answer 1

7

Copying To A TTY (Not Stdout!)

Pipeline components run in subshells, so even if they do assign shell variables (and the syntax for that was wrong), those shell variables are unset as soon as the pipeline exits (since the subshells only live as long as the pipeline does).

Thus, you need to capture the output of the entire pipeline into your variable:

var=$(find "$filename" -perm "$i" | tee /dev/tty | wc -l)

Personally, btw, I'd be teeing to /dev/stderr or /dev/fd/2 to avoid making behavior dependent on whether a TTY is available.


Actually Piping To Stdout

With bash 4.1, automatic file descriptor allocation lets you do the following:

exec {stdout_copy}>&1 # make the FD named in "$stdout_copy" a copy of FD 1

# tee over to "/dev/fd/$stdout_copy"
var=$(find "$filename" -perm "$i" | tee /dev/fd/"$stdout_copy" | wc -l)

exec {stdout_copy}>&- # close that copy previously created
echo "Captured value of var: $var"

With an older version of bash, you'd need to allocate a FD yourself -- in the below example, I'm choosing file descriptor number 3 (as 0, 1 and 2 are reserved for stdin, stdout and stderr, respectively):

exec 3>&1  # make copy of stdout

# tee to that copy with FD 1 going to wc in the pipe
var=$(find "$filename" -perm "$i" | tee /dev/fd/3 | wc -l)

exec 3>&-  # close copy of stdout
Sign up to request clarification or add additional context in comments.

2 Comments

Yes, I tried that as well, but how to get only number out of that variable then?
Eh? The variable only has the number (and maybe a leading tab, if it's a BSD version of wc you're running) already. Command substitutions only capture stdout -- anything you sent to stderr or straight to a TTY bypasses them.

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.