0

I'm trying to loop same variables from multiple files in bash. Here's my file structure and their contents;

  • script.sh
  • first.conf
  • second.conf

Inside of first.conf;

var=Hello1

Inside of second.conf;

var=Hello2

Inside of script.sh;

#!/bin/bash

_a=`find ~/ -name "*.conf"`

source ${_a}

for x in ${_a}
do
  echo "$var"
done

This might look really dumb tho, I'm really new to programming.

What I'm trying to do is to loop and echo these $vars from 2 different configs.

How can I do that?

2
  • Storing output from find in a single string isn't always a great idea in and of itself. Look at what happens when your filenames have spaces, or -- even worse -- if the files contain newlines. find returns multiple results; an array is the native approach to storing several strings in bash (and using NUL rather than newline delimiters lets you distinguish between newlines added as separators by find and newlines that are part of the filenames themselves). Commented Dec 20, 2018 at 17:14
  • Thank you for your wise words, I'll definitely take them into consideration during my learning process. 👍 Commented Dec 20, 2018 at 17:19

2 Answers 2

1

Consider:

while IFS= read -r -d '' conf; do
  (source "$conf" && echo "$var")
done < <(find ~ -name '*.conf' -print0)

Breaking down how this works:

  • The while read syntax is discussed in BashFAQ #1. The variant with -d '' expects input separated by NULs rather than newlines -- more on that later.
  • Putting (source "$conf" && echo "$var") in parens prevents side effects on the rest of your script -- while this has a performance cost, it ensures that variables added by source are only present for the echo. Using the && prevents the echo from running if the source command fails.
  • <(...) is process substitution syntax; it's replaced with a filename that can be read to retrieve the output of the command therein (in this case find). Using this syntax rather than piping into the loop avoids the bugs discussed in BashFAQ #24.
  • The -print0 action in find prints the name of the file found, followed by a NUL character -- a zero byte. What's useful about NUL bytes is that, unlike any other ASCII character, they can't exist in UNIX paths; using them thus prevents your code from being subject to trickery (think about someone running d=$'./tmp/\n/etc/passwd\n' && mkdir -p -- "$d" && touch "$d/hi.conf" -- traditional find output would have /etc/passwd showing up on its own line, but with find -print0, the newlines in the name aren't mistaken for a separator between files.
Sign up to request clarification or add additional context in comments.

Comments

0

This is a shorter and simpler way.

#!/bin/bash

for f in *.conf
do
        source "$f"; echo "$f : $var"
done

2 Comments

source "$f" to be reliable with files that have spaces in their names. And without either find or shopt -s globstar followed by **/*.conf, it won't search subdirectories (which the OP's original code did).
Thanks for the comments Charles.}

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.