Variables value gets lost in subshell
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.
精彩评论