spaces in bash scripts - bash

So, I've been trying for a while and this really won't work. I'm trying to write a script that will concatenate many pdf files into one without the tedium of specifying them at the command-line interface (they all have similar names).
#!/bin/bash
i=1
list="science.pdf"
outputfile="file.pdf"
while [ $i -le 3 ]; do
file="science (${i}).pdf"
list="$list $file"
let i=i+1
done
pdftk $list cat output $outputfile
And this is my output:
sean#taylor:~/Downloads/understanding/s0$ ./unite.sh
Error: Failed to open PDF file:
science
Error: Failed to open PDF file:
(1).pdf
Error: Failed to open PDF file:
science
Error: Failed to open PDF file:
(2).pdf
Error: Failed to open PDF file:
science
Error: Failed to open PDF file:
(3).pdf
Errors encountered. No output created.
Done. Input errors, so no output created.
I figure that somehow the script thinks that the files should be split up wherever therre's a space, but I've tried both a backslash before the space (\ ) and surrounding the file name with a quote (\") to no avail.
Can anyone help please?

Don't append the filenames to a string. Use an array instead:
i=1
list=( "science.pdf" )
outputfile="file.pdf"
while [ $i -le 3 ]; do
file="science (${i}).pdf"
list+=( "$file" )
let i=i+1
done
pdftk "${list[#]}" cat output $outputfile
You can also simplify your script further by using a for-loop as shown below:
list=( "science.pdf" )
for (( i=1; i<=3; i++ )); do
file="science (${i}).pdf"
list+=( "$file" )
done
pdftk "${list[#]}" cat output $outputfile

When you execute your final command
pdftk $list cat output $outputfile
The $list variable is no longer quoted, i.e., what is actually being executed is
pdftk science.pdf science (1).pdf ... science (3).pdf cat output file.pdf
You need to super-quote your $list variable. Try:
while [ $i -le 3 ]; do
file="science (${i}).pdf"
list="$list \"$file\""
let i=i+1
done
You may need to use a different method of concatenating variables as your loop will probably continuously unquote the previously concatenated values.

Related

Bash - File content

I am trying to store content of multiple files to variable, but can't find the proper way. Example follows - I need to rewrite last line somehow as $content is not a file.
content="";
if (($# > 0)); then #input files given as arguments
for i ; do
if [ -r "${i}" ]; then
cat "${i}" >> "$content" #I need to append contents to existing variable
No need for a loop; the purpose of cat is to catenate multiple files.
content=$(cat "$#")
If you do want a loop for some reason, the conditional is superfluous; a loop over zero elements will execute zero times.
content=$(for i; do
test -r "$i" && cat "$i"
done)
In Bash, you can append to a variable with +=; more portably, you can do var="${var}newvalue".

Bash function parameter assignment to read a file (list) and write a file (array)

I am trying to write a bash function I can call regularly from within a larger set of scripts. I want to pass this function the name of a file containing a plain list of text strings:
blue
red
green
... and have the function write out these strings to a different file (the name of which is also passed as parameter to the function) in bash-compatible array format:
[Bb]lue [Rr]ed [Gg]reen
I can't get the function to (internally) recognise the name of the output file being passed. It throws an "ambiguous redirect" error and then a bunch of "No such file or directory" errors after that. It is however processing the input file OK. The problem appears be how I am assigning the parameter to a local string in the function. Unfortunately I have changed the loc_out= line in the function so many times that I can no longer recall all the forms I have tried. Hopefully the example is clear, if not best practise:
process_list () {
# assign input file name to local string
loc_in=(${1});
# assign output file name to local string
loc_out=($(<${2})); # this is not right
while read line
do
echo "loc_out before: $loc_out";
echo "loc_in term: $line";
item_length=${#line};
# loop until end of string
for (( i=0; i<$item_length; i++ ));
do
echo "char $i of $line: ${line:$i:1}";
# write out opening bracket and capital
if [ ${i} -eq 0 ]; then
echo -e "[" >> $loc_out;
echo -e ${line:$i:1} | tr '[:lower:]' '[:upper:]' >> "${loc_out}";
fi;
# write out current letter
echo -e ${line:$i:1} >> "${loc_out}";
# write out closing bracket
if [ ${i} -eq 0 ]; then
echo -e "]" >> "${loc_out}";
fi;
done;
# write out trailing space
echo -e " " >> "${loc_out}";
# check the output file
echo "loc_out after: ${loc_out}";
done < $loc_in;
}
f_in="/path/to/colour_list.txt";
f_out="/path/to/colour_array.txt";
echo "loc_in (outside function): ${loc_in}";
echo "loc_out (outside function): ${loc_out}";
process_list $f_in $f_out;
Any assistance on what I am doing wrong would be much appreciated.
Change:
loc_out=($(<${2})); # this is not right
To this:
loc_out=(${2}); # this should be right
You want in that line just the file name.
Hopefully this will solve your problem.
EDIT:
Besides you could/should write this:
loc_in=${1};
loc_out=${2};
You do not need parantheses, as far as I understand.

Load List From Text File To Bash Script

I've a .txt file which contains
abc.com
google.com
....
....
yahoo.com
And I'm interested in loading it to a bash script as a list (i.e. Domain_List=( "abc.com" "google.com" .... "yahoo.com") ). Is it possible to do?
Additional information, once the list is obtained it is used in a for loop and if statements.
for i in "${Domain_list[#]}
do
if grep -q "${Domain_list[counter]}" domains.log
....
....
fi
....
let counter=counter+1
done
Thank you,
Update:
I've changed the format to Domain_list=( "google.com .... "yahoo.com" ), and using source Doamin.txt allows me to use Domain_list as a list in the bash script.
#!/bin/bash
counter=0
source domain.txt
for i in "${domain_list[#]}"
do
echo "${domain_list[counter]}"
let counter=counter+1
done
echo "$counter"
Suppose, your datafile name is web.txt. Using command substitution (backtics) and cat, the array can be built. Pl. see the following code,
myarray=(`cat web.txt`)
noofelements=${#myarray[*]}
#now traverse the array
counter=0
while [ $counter -lt $noofelements ]
do
echo " Element $counter is ${myarray[$counter]}"
counter=$(( $counter + 1 ))
done
Domain_list=()
while read addr
do
Domain_list+=($addr)
done < addresses.txt
That should store each line of the text file into the array.
I used the source command, and it works fine.
#!/bin/bash
counter=0
source domain.txt
for i in "${domain_list[#]}"
do
echo "${domain_list[counter]}"
let counter=counter+1
done
echo "$counter"
There's no need for a counter if we're sourcing the list from a file. You can simply iterate through the list and echo the value.
#!/bin/bash
source domain.txt
for i in ${domain_list[#]}
do
echo $i
done

Shell Script: Concatenate string while interating

I am writing a shell script that iterates over directory content and searches for pdf files and creates a string listing all pdf files in it e.g. "pdffile1.pdf pdffile2.pdf pdffile3.pdf".
pdffiles=""
#get files in pdf directory
for filename in $1/*; do
fn=$(basename "$filename")
#check if file exist
if [ -f "$filename" ]; then
#grab only pdf files
if [ ${filename: -4} == ".pdf" ]; then
pdffiles = $filename $pdffiles
fi
fi
done
The thing is this code pdffiles = $filename $pdffiles is wrong and shell script outputs following error message ./mergepdfs.sh: line 39: pdffiles: command not found.
What is wrong with that line?
Don't use spaces around '=' when assigning:
x = 1 # execution of x with arguments '=' and '1'
x=1 # setting shell variable x
Why not simply:
pdffiles=$1/*.pdf
If you like to get them in array form:
pdffiles=($1/*.pdf)
Output all:
echo ${pdffiles[*]}
Output size:
echo ${#pdffiles[*]}
Output a single name:
echo ${pdffiles[4]}
I think you don't need space around =. This must be a correct line:
pdffiles=$filename' '$pdffiles

Why doesn't my variable seem to increment in my bash while loop?

I am fairly new to bash scripting. I can't seem to get the correct value of my counting variables to display at the end of of a while loop in my bash script.
Background: I have a fairly simple task: I would like to pass a text file containing a list of file paths to a bash script, have it check for the existence of those files, and count the number of existing/missing files. I got most of the script to work, except for the counting part.
N=0
correct=0
incorrect=0
cat $1 | while read filename ; do
N=$((N+1))
echo "$N"
if ! [ -f $filename ]; then
incorrect=$((incorrect+1))
else
correct=$((correct+1))
fi
done
echo "# of Correct Paths: $correct"
echo "# of Incorrect Paths: $incorrect"
echo "Total # of Files: $N"
If I have a list of 5 files, 4 of which exist, I expect to get the following output (note the echo command within the while loop):
1
2
3
4
5
# of Correct Paths: 4
# of Incorrect Paths: 1
Total # of Files: 5
Instead, I get:
1
2
3
4
5
# of Correct Paths: 0
# of Incorrect Paths: 0
Total # of Files: 0
What happened to the values of these variables? Google had many suggestions of questionable quality and I think I could get it to work with a little more searching, but a brief explanation of what I'm doing wrong would be very helpful.
This is because you are using the useless cat command with a pipe, causing a subshell to be created. Try it without the cat:
while read filename ; do
N=$((N+1))
....
done < file
Alternatively, if you want to keep the cat for some reason, you can fix your script simply by adding this line before the cat instruction:
shopt -s lastpipe
More generally, sometimes you want to pipe the output of a command. Here's an example that uses process substitution to lint JavaScript files about to be committed by Git and counts the number of files that failed:
# $# glob
git-staged-files() {
git diff --cached -C -C -z --name-only --relative --diff-filter=ACMRTUXB "$#"
}
# $# name
map() { IFS= read -rd $'\0' "$#"; }
declare -i errs=0
while map file; do
echo "Checking $file..."
git show ":$file"|
eslint --stdin --stdin-filename "$file" || ((++errs))
done < <(git-staged-files \*.js)
((errs)) && echo -en "\e[31m$errs files with errors.\e[00m " >&2 || :

Resources