Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

First off, sorry for the title. I'm not sure of the correct terminology so if anyone can improve upon it that would be good.

I am trying to write a simple bash function that takes, as it's arguments, a number of files and/or directories. It should:

  1. Fully qualify the filenames.
  2. Sort them.
  3. Remove duplicates.
  4. Print all that actually exist.
  5. Return the number of non existant files.

I have a script that almost does what I want, but falls down on the sorting. The return value of the script as it stands is correct, but the output is not (unsorted and duplicates). If I uncomment the | sort -u statement as indicated, the output is correct but the return value is always 0.

N.B. Simpler solutions to solve the problem are welcome but the question is really about why is this occuring in the code I have. That is, why does adding the pipe seemingly stop the script incrementing the variable r?

Thankyou for your help. Here's the script:

function uniqfile
{
    local r=0 

    for arg in "$@"
    do  
        readlink -e "$arg" || (( ++r ))

    done #| sort -u    ## remove that comment

    return $r
}
share|improve this question
    
Just a small observation. You can reduce for arg in "$@" to for arg. "If 'in WORDS ...;' is not present, then 'in "$@"' is assumed." - help for –  manatwork Sep 30 '11 at 9:35
    
@manatwork Thanks. That's good to know. –  tjm Sep 30 '11 at 9:58

2 Answers 2

up vote 11 down vote accepted

This is a well known bash pitfall, due to this feature:

Each command in a pipeline is executed as a separate process (i.e., in a subshell).

so that modified variables are local to the subshell, and not visible once back in the parent.

To avoid that, rephrase your code to avoid the pipeline, with a process substitution:

 for arg in "$@"
    do  
        readlink -e "$arg" || (( ++r ))

    done > >(sort -u)
share|improve this answer
    
Thankyou. That's great. I wonder if you could tell me the name of the >(..command..) construct. I think I know how it works but feel I should do some further reading. –  tjm Sep 30 '11 at 9:07
1  
@tjm: it is called process substitution –  enzotib Sep 30 '11 at 9:15

The "| sort -u" forces the preceeding bit (so the whole for loop) to run in a sub-process (bash needs a 'stdout' to redirect into the sort 'stdin'. (Internet seems to think 'ksh' and 'bash' handle this case slightly differently .. first or last command in the pipe sequence gets put into a subshell?)

This thread goes over a similar problem, and has a neat solution at the end: http://ubuntuforums.org/showthread.php?t=312017

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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