Bash: For Each Line in a File, Set variable - linux

I am trying to iterate through a text file, and set a variable once for each line.
I am having trouble doing so. I have simplified the process so that it is just echoing the result. There would be more going one, but this part of the widget is not working correctly.
while read p; do
set $FN=`echo $p`
echo $FN
done <file.txt
When I don't use set, it looks like it is trying to echo a file called the contents of $p.
I am using echo $p to send the line to a series of cuts, to get the data I want.
I know I am doing something fundamentally wrong, just not sure what that might be.

This works for me. Just say "FN=" without the "set
while read p; do
FN=$p
echo $FN
done < test.list

Related

Bash write into file that is named by a variable

I am working on creating a simple script to make it easy to set up a virtual host on Apache server. Currently I don't seem to be able to write the config file because it's in a variable. How do I get around it. Here is my code that does not work.
siteConf="/etc/apache2/sites-available/$domain.conf"
echo "creating conf file"
echo "<VirtualHost *:80>" >> $siteConf
echo "ServerName *.$domain" >> $siteConf
echo "DocumentRoot $publicHtmlLoc" >> $siteConf
echo "DirectoryIndex index.php" >> $siteConf
echo "ServerAlias $database.newphp.junglecoders.dk" >> $siteConf
echo "</VirtualHost>" >> $siteConf
I am running the script with the bash command.
Edit: The Error i get is this
$siteConf: ambiguous redirect
Domain comes from here:
echo "Write websiter url example.com, no sub dir allowed"
read -p "Name: " domain
As suggested in comments its the path that is wrong, i tried to echo out the variable and i can see it has removed the '.' chars from some reason and left a space instead, why would the script do that ?
Edit2:
Was using IFS earlier in the script, to split the the domain name
The code looks as if it should work. You might do better with
{
echo "<VirtualHost …>"
…
echo "</VirtualHost>"
} > $siteConf
(or >> $siteConf if you really want to add to the existing file). That does a single redirection for all the output, and truncates the file. It is also a good idea as a general rule to enclose uses of variables in double quotes:
} > "$siteConf"
Basic debugging for shell scripts:
What do you get from bash -x your-script.sh?
You get 'ambiguous redirect' when the variable named doesn't exist or is empty, or if it expands to two or more words. That suggests that the first line of your script isn't an accurate representation of what you've got, but it is hard to guess how you've got it wrong. Misspelled name, or an unwanted space are probably the most likely, but it could be something else.
Alright looks like I needed to wrap it like in quotes like in the answer Getting an 'ambiguous redirect' error that antak suggested.
That's a good question to cross-reference. At one point, this question was closed as a duplicate of it, but the issue here turned out to be about IFS being set, which is not an alternative source of trouble identified in that other question.
Have you gone messing with IFS at any point in the script?
Yes — been doing IFS for splitting the URL into parts.
Note that:
set_with_spaces="name1 name2"
echo Hi >> $set_with_spaces
yields
bash: $set_with_spaces: ambiguous redirect too.
Two names were generated from one variable.
$ IFS=.
$ domain=abc.def.ghi.jkl
$ echo $domain
abc def ghi jkl
$ echo "$domain"
abc.def.ghi.jkl
$ IFS=$' \t\n'
$ echo $domain
abc.def.ghi.jkl
$
If you have been messing with IFS, then its current value is probably altering how the names are being interpreted. Reinstate it to its default value (blank, tab, newline):
IFS=$' \t\n'

bash prevent escaping with echo

I am relatively new to linux and am trying to create a TikZ figure parsing a file. In order to do so I read in the file with a $%&-bash script containing the following statement
echo "\fill[color=blue] ($xp,$zp) circle (5pt);" >> $fout
this results in the following output
^Lill[color=blue] ($xp,$zp) circle (5pt);
Obviously echo escapes the \f and I did not find a way around it.
I have tried all options like "-e" "-n" and what have you, have tried all kinds of combinations of " ' etc, but to no avail.
I am stuck as so often with linux, but this time even google didn't help (OMG=Oh My Google!!!!!!!!).
echo should not do backslash escapes by default, unless -e is specified. You can try echo -E to force turning them off (in case you have aliased echo to echo -e or something).
Alternatively, try using single quotes (although now that I think about it, I don't see how it would help):
echo '\fill[color=blue] ('"$xp,$zp"') circle (5pt);' >> $fout

Looping through lines in a file in bash, without using stdin

I am foxed by the following situation.
I have a file list.txt that I want to run through line by line, in a loop, in bash. A typical line in list.txt has spaces in. The problem is that the loop contains a "read" command. I want to write this loop in bash rather than something like perl. I can't do it :-(
Here's how I would usually write a loop to read from a file line by line:
while read p; do
echo $p
echo "Hit enter for the next one."
read x
done < list.txt
This doesn't work though, because of course "read x" will be reading from list.txt rather than the keyboard.
And this doesn't work either:
for i in `cat list.txt`; do
echo $i
echo "Hit enter for the next one."
read x
done
because the lines in list.txt have spaces in.
I have two proposed solutions, both of which stink:
1) I could edit list.txt, and globally replace all spaces with "THERE_SHOULD_BE_A_SPACE_HERE" . I could then use something like sed, within my loop, to replace THERE_SHOULD_BE_A_SPACE_HERE with a space and I'd be all set. I don't like this for the stupid reason that it will fail if any of the lines in list.txt contain the phrase THERE_SHOULD_BE_A_SPACE_HERE (so malicious users can mess me up).
2) I could use the while loop with stdin and then in each loop I could actually launch e.g. a new terminal, which would be unaffected by the goings-on involving stdin in the original shell. I tried this and I did get it to work, but it was ugly: I want to wrap all this up in a shell script and I don't want that shell script to be randomly opening new windows. What would be nice, and what might somehow be the answer to this question, would be if I could figure out how to somehow invoke a new shell in the command and feed commands to it without feeding stdin to it, but I can't get it to work. For example this doesn't work and I don't really know why:
while read p; do
bash -c "echo $p; echo ""Press enter for the next one.""; read x;";
done < list.txt
This attempt seems to fail because "read x", despite being in a different shell somehow, is still seemingly reading from list.txt. But I feel like I might be close with this one -- who knows.
Help!
You must open as a different file descriptor
while read p <&3; do
echo "$p"
echo 'Hit enter for the next one'
read x
done 3< list.txt
Update: Just ignore the lengthy discussion in the comments below. It has nothing to do with the question or this answer.
I would probably count lines in a file and iterate each of those using eg. sed. It is also possible to read infinitely from stdin by changing while condition to: while true; and exit reading with ctrl+c.
line=0 lines=$(sed -n '$=' in.file)
while [ $line -lt $lines ]
do
let line++
sed -n "${line}p" in.file
echo "Hit enter for the next ${line} of ${lines}."
read -s x
done
AWK is also great tool for this. Simple way to iterate through input would be like:
awk '{ print $0; printf "%s", "Hit enter for the next"; getline < "-" }' file
As an alternative, you can read from stderr, which by default is connected to the tty as well. The following then also includes a test for that assumption:
(
tty -s <& 2|| exit 1
while read -r line; do
echo "$line"
echo 'Hit enter'
read x <& 2
done < file
)

Linux: Use parameter as file shortcut

Cheers everyone :)
I'm trying to make a linux script. This script shall be called with one parameter, a file stored in my home directory. I can't seem to use
cat $var1 >> $1
So i have this variable $var1 and I want to save it in a file that does exist and its name is given in $1
Anyone help me please!
The cat command shows the contents of a file.
Unless the value of $var1 is a file that you want to 'copy' to $1, it won't work (probably gives a 'file not found' kind of error).
The easiest solution, I can think of, is to echo the variable:
echo "$var1" >> "$1"
As stated by #glglgl it is better to put the variable between double quotes. They prevent spaces messing up the command as they split a parameter into multiple parameters
Then you want to create a script that saves things in the file you indicate as a parameter.
file_name=$1 #you get the parameter
...do things...
echo "everything you've done" >> $file_name #case want to append
echo "everything you've done" > $file_name #case want to overwrite

How to read from user within while-loop read line?

I had a bash file which prompted the user for some parameters and used defaults if nothing was given. The script then went on to perform some other commands with the parameters.
This worked great - no problems until most recent addition.
In an attempt to read the NAMES parameter from a txt file, I've added a while-loop to take in the names in the file, but I would still like the remaining parameters prompted for.
But once I added the while loop, the output shows the printed prompt in get_ans() and never pauses for a read, thus all the defaults are selected.
I would like to read the first parameter from a file, then all subsequent files from prompting the user.
What did I break by adding the while-loop?
cat list.txt |
while read line
do
get_ans "Name" "$line"
read NAME < $tmp_file
get_ans "Name" "$line"
read NAME < $tmp_file
done
function get_ans
{
if [ -f $tmp_file ]; then
rm $tmp_file
PROMPT=$1
DEFAULT=$2
echo -n "$PROMPT [$DEFAULT]: "
read ans
if [ -z "$ans" ]; then
ans="$DEFAULT"
fi
echo "$ans" > $tmp_file
}
(NOTE: Code is not copy&paste so please excuse typos. Actual code has function defined before the main())
You pipe data into your the while loops STDIN. So the read in get_ans is also taking data from that STDIN stream.
You can pipe data into while on a different file descriptor to avoid the issue and stop bothering with temp files:
while read -u 9 line; do
NAME=$(get_ans Name "$line")
done 9< list.txt
get_ans() {
local PROMPT=$1 DEFAULT=$2 ans
read -p "$PROMPT [$DEFAULT]: " ans
echo "${ans:-$DEFAULT}"
}
To read directly from the terminal, not from stdin (assuming you're on a *NIX machine, not a Windows machine):
while read foo</some/file; do
read bar</dev/tty
echo "got <$bar>"
done
When you pipe one command into another on the command line, like:
$ foo | bar
The shell is going to set it up so that bar's standard input comes from foo's standard output. Anything that foo sends to stdout will go directly to bar's stdin.
In your case, this means that the only thing that your script can read from is the standard output of the cat command, which will contain the contents of your file.
Instead of using a pipe on the command line, make the filename be the first parameter of your script. Then open and read from the file inside your code and read from the user as normal.

Resources