开发者

Variables value gets lost in subshell

开发者 https://www.devze.com 2023-01-04 17:29 出处:网络
This bash script concatenates the names for jar files to a classpath (variable CP), in the while loop the value is correct but is lost in the subshell as descibed in this related question Bash variabl

This bash script concatenates the names for jar files to a classpath (variable CP), in the while loop the value is correct but is lost in the subshell as descibed in this related question Bash variable scope

#!/bin/bash
CP="AAA"
func() {
        ls -1 | while read JAR
        do
                if [ ! -z "$CP"开发者_StackOverflow中文版 ]; then
                        CP=${CP}':'
                fi
                CP=${CP}${JAR}
        done
        echo $CP # <-- prints AAA
}

func

My question is, since I can't figure out which element will be the last one, how can the value be saved.

Do I actually have to save the current value (repeatedly in the loop) to a file?

EDIT:

A colleague came up with this command sequence which works well

ls | xargs echo|tr ' ' :


The issue here is that using while in a pipeline creates a subshell, and a subshell cannot affect its parent. You can get around this in a few ways. For what you are doing now, this will suffice:

for JAR in *; do
    # Your stuff
done

Another thing to note is that you shouldn't rely on parsing ls

This also shows you ways to avoid the subshell.


You might find using find a bit more versatile.

For example:

export CP=$( find /home/depesz/q/ -maxdepth 1 -type f -name '*.jar' -printf ':%p' | cut -b 2- )

Of course set of find options is dependant on what you need/want.

This one is closer to what you had previously:

export CP=$( find . -maxdepth 1 -type f -name '*.jar' -printf ':%f' | cut -b 2- )


Implicit subshells are confusing; always make them explicit by using parentheses. To solve your problem, just move the echo inside the subshell.

#!/bin/bash
CP="AAA"
func() {
  ls -1 | (
    while read JAR
    do
      if [ ! -z "$CP" ]; then
        CP=${CP}':'
      fi
      CP=${CP}${JAR}
    done
    echo $CP
  )
}
func


Here is another solution, which avoid the spawning of a subshell altogether. The key is to capture the loop input into a variable (in my example called "JARS") and then redirect this variable into the loop using << EOF:

JARS=$(ls -1)

while read JAR
do
        if [ ! -z "$CP" ]; then
                CP=${CP}':'
        fi
        CP=${CP}${JAR}
done <<EOF
$JARS
EOF

echo $CP


None of these answers seem to get into actually returning the value properly, echoing out the answer is fine with a very simple routine, but say you want output of the script in action, this is useless. I don't have the best answer but I don't have much time to figure it out either, so I'm just going to suggest to output exactly what you want to a temp file and read that (surprised it hasn't been mentioned to date) :

#!/bin/bash
CP="AAA"
func() {
  ls -1 | (
    while read JAR
    do
      if [ ! -z "$CP" ]; then
        CP=${CP}':'
      fi
      CP=${CP}${JAR}
    done
    echo "$CP" > /tmp/cp-value-file
  )
}

func
CP=$(cat /tmp/cp-value-file)
echo $CP

Downside: will in some cases need to write to the disk every iteration of the loop.

0

精彩评论

暂无评论...
验证码 换一张
取 消