Shell script: parse blank spaces correctly - linux

In the code below I was expecting an output like this:
Transmission_Downloads/The.Last.Man.On.Earth.S02E04.HDTV.x264-KILLERS[ettv]/Torrent-Downloaded-from-ExtraTorrent.cc.txt
Transmission_Downloads/Fargo.S02E01.HDTV.x264-KILLERS[ettv]/Torrent-Downloaded-from-ExtraTorrent.cc.txt
Transmission_Downloads/Nightwish - Showtime Storytime (2013) 1080p 5.1/SEMEAR.txt
But what I do got was this
Transmission_Downloads/The.Last.Man.On.Earth.S02E04.HDTV.x264-KILLERS[ettv]/Torrent-Downloaded-from-ExtraTorrent.cc.txt
Transmission_Downloads/Fargo.S02E01.HDTV.x264-KILLERS[ettv]/Torrent-Downloaded-from-ExtraTorrent.cc.txt
Transmission_Downloads/Nightwish
-
Showtime
Storytime
(2013)
1080p
5.1/SEMEAR.txt
What should I do for my shell script parse blank spaces on file and folder names correctly? Thanks!
#!/bin/bash
#opcao -s nao mostra o texto na tela
echo "Digite sua senha"
read -s answer
root_folder="/home/pi/"
user="pi"
server="*****"
local_folder="/home/dellcloud2"
#Colocar aqui os paths dos arquivos que deseja puxar
array=(
"Transmission_Downloads/The.Last.Man.On.Earth.S02E04.HDTV.x264-KILLERS[ettv]/Torrent-Downloaded-from-ExtraTorrent.cc.txt"
"Transmission_Downloads/Fargo.S02E01.HDTV.x264-KILLERS[ettv]/Torrent-Downloaded-from-ExtraTorrent.cc.txt"
"Transmission_Downloads/Nightwish - Showtime Storytime (2013) 1080p 5.1/SEMEAR.txt"
)
for i in ${array[#]}; do
#sshpass -p $answer rsync -aP $user#$server:$root_folder$i $local_folder
echo $i
done

You need to use "${array[#]}" to preserve white space. The quotes around the array plus the wildcard # together enable this.
This also works with positional parameters, i.e. "$#" gives you an array of the command line arguments, properly quoted as they were given.

Related

Inserting the date into a "MAIL" message body in a BASH script

I have a relatively simple BASH script to send mail from my Raspberry Pi. The first argument is the Subject line and the second is a string of data files to be attached.
It is basically working when I specify the message body as a file (line 6). But if I try to create a text sting containing the date as the message body it fails (line7). Here is my script:
#!/bin/bash
#echo $2
# To
TO="me#hotmail.com"
# Message
MESSAGE="output/MessageBody.txt"
MESSAGEx="Midnight `date '+%Y-%m-%d %H:%M:%S %Z'` Pi report"
echo $MESSAGE
echo $MESSAGEx
temp=$(echo $2 | tr ";" "\n")
declare -a attargs
for att in $temp; do
attargs+=( "-A" "$att" )
done
# Sending email using /bin/mail
/usr/bin/mail -s "$1" "$TO" ${attargs[#]} < $MESSAGEx
Here is the output from this command
/usr/pgms/sendtome.sh "test message" "/mnt/usbdrive/output/JSONstart.txt;/mnt/usbdrive/output/Outback_error.log;/mnt/usbdrive/output/OutbackReaderPrint.txt"
when I specify MESSAGEx as the message body:
/mnt/usbdrive/output/MessageBody.txt
Midnight 2019-08-14 07:40:31 MDT Pi report
/usr/pgms/sendtome.sh: line 22: $MESSAGEx: ambiguous redirect
If I use MESSAGE, ie the text file reference, it works.
How can it create a message body text paragraph which contains the date or some other item? Thanks....RDK
There's a number of issues here.
You should generally quote strings. Without quoting, the string after < is split (hence the error message) and the array you took so much care to collect will lose its purpose.
The thing after < needs to be the name of a file. In Bash you can use a here string <<<"$MESSAGEx" but the common and simple portable solution is to echo (or better printf) its value into a pipe.
You should prefer lower case for your private variable names, but this is mainly a stylistic recommendation. (There are reserved variables like PATH and SHELL which you really don't want to clobber; POSIX reserves upper case variable names for system use.)
Here's a refactoring which attempts to address these concerns.
#!/bin/bash
to="me#hotmail.com"
# Message
#msgfile="output/MessageBody.txt"
msgbody="Midnight `date '+%Y-%m-%d %H:%M:%S %Z'` Pi report"
#echo "$msgfile"
#echo "$msgbody"
declare -a attargs
for att in $(echo "$2" | tr ";" "\n"); do
attargs+=( "-A" "$att" )
done
/usr/bin/mail -s "$1" "${attargs[#]}" "$to"<<< "$msgbody"
Perhaps a better design would be to just shift the first argument and then use "$#" as the list of files to attach.

select a string from file csh

I have a file file.dat as follow:
1.1,2.1 1.4
3.1,2.1 2.4
2.4,4.5 11.5
..
And I want to select each time the whole line (string) and replace it in another file. So far I tried the following
#!/bin/csh
set FILENAME = 'file.dat' # file in which the strings are
set str = "229.8,230.9 230.36" # initialize the first string
set n = 1
while ( $n <= 3 ) # number of lines in the FILENAME
echo Testing the first string $str
set rep = $(head -n $n "$FILENAME")
# n++ # increment the index
end
When I tried to launch the script csh launch.sh I obtained the follow error message
Testing the first string 229.8,230.9 230.36
Illegal variable name. # connect with the rep definition(?)
The file in which I want to change the string str is as follow (this is btw a secondary problem which I could figure out by myself once I understand what's wrong in the first lines):
# Name Type Par
Mi FI 154.2355189465
So UN 229.8,230.9 230.36 # line to be changed
Za FI 0.8000020209
May somebody help me, please?
$(...) is Bash syntax for command substitution in Bash.
In C-shell you have to use backticks instead (yuck).

Bash Shell - problems with quoting

I have a problem in this line:
--description=\""$(<$varLeer/media/festplatte/txt/$varDesc)\""
I want it to get out the following:
--description="$(<$varLeer/media/festplatte/txt/$varDesc)"
But how do I get it that it does not "make use" of the letters "$(< and )" ?
I hope you understand what I mean. I read this tutorial (http://wiki.bash-hackers.org/syntax/quoting) but I don't understand how I can write down the "$(< and the )" without the shell using it "as Code".
To get maybe a bit clearer, I want to get the second code to be shwon but with the "$(< )" but marked as "real code".
What I tried is this - but it did not work:
--description=\"$(<"$varLeer/media/festplatte/txt/$varDesc\")"
//EDIT: Lets be much clearer at what I want to do.
There is a phyton script called "youtube-uploader" (https://code.google.com/p/youtube-upload/wiki/Readme). Now that I don't have to write "youtube-upload ....... INSERT VERY MUCH HERE ;)" every video I want that the uploaded parts get their title, description etc automatically. My idea was to just open my ytscript.bash with nano, edit the first top variables and then just do "sh ytscript.bash", insert my password and relax.
The part would be +1 every time the loop is used and with this there is automatically an own fitting title for every part. Here is my code so far:
#!/bin/bash
# Variablen
varSpielart="Lets Play: "
varSpielname="Diablo 3 "
varParttext="- PART "
varStartpart=1
varEndpart=2
varExtras=" [PS4][1080p]"
varKategorie=Games
varDesc="lpd3desc.txt"
varDateiname=lpd3
varDateiendung=.m4v
varKeywords="ps4, spieleule, piupload"
varPlaylist="UNDEFINIERT"
varAnzahlparts=`expr $varEndpart - $varStartpart`
varLeer=" "
echo "Gebe dein Passwort ein!"
read varPasswort
echo YT-UPLOAD: BEGINNE NEUEN UPLOAD
echo YT-UPLOAD: SPIEL $varSpielname
echo YT-UPLOAD: ANZAHL PARTS: $varAnzahlparts
varDurchgang=$varStartpart
while [ $varEndpart -gt $varDurchgang ]
do
cd youtube-upload
youtube-upload --email="email#email.de" --password=$varPasswort --private --category=$varKategorie --title=\""$varSpielart$varSpielname$varParttext$varDurchgang$varExtras\"" \
--description=$(<$varLeer/media/festplatte/txt/$varDesc) \
/media/festplatte/upload/$varDateiname"p"$varDurchgang""$varDateiendung &
varDurchgang=`expr $varDurchgang + 1`
echo Durchgang: $varDurchgang
echo Entpart: $varEndpart
echo Startpart: $varStartpart
echo Anzahlparts: $varAnzahlparts
done
The automatically title works like a charm (except giving me a " at the start and the end of the title on youtube.com without me wanting it! :( ). The description on youtube.com only gets a "" in it with the first answer under this question.
Try single quotes:
--description='"$(<$varLeer/media/festplatte/txt/$varDesc)"'
Try this
temp='$(<'$varLeer'/media/festplatte/txt/'$varDesc')'
--description=$temp

String Concatenation error in Cygwin bash shell

I am having problems concatenate two strings in BASH (I am using Cygwin)
When I am doing it step by step in the cygwin window, it works.
i.e by defining dt=2012-12-31 and c=.txt explicitly and then concatenating in filename=${dt}${c}.
It doesn't seem to work when i am running it through my script where these variables are defined by cutting and assigning values from content of a file.
Though the variables are assigned with the same values as above, the concatenation in this case doesn't work.
instead of 2012-12-31.txt i am getting .txt-12-31 as result.
The code is:
for x in {0..11}
do
IFS=$'\n'
filename=date_list.txt
file=($(<"$filename"))
IFS=$'\t\n'
dt=${file[$x]}
echo $dt
for y in {0..85}
do
IFS=$'\n'
filename=SQL_Mnemonics.txt
file=($(<"$filename"))
IFS=$'\t\n'
Mn=${file[$y]}
for k in {3..502}
do
IFS=$'\n'
c=.txt
filename=${dt}${c}
file=($(<"$filename"))
IFS=$'\t\n'
echo ${file[$k]} > temp_file.txt
cusip=`cut -c11-19 temp_file.txt`
result=$(sh ctest.sh $Mn, $dt, $cusip)
echo "$result" > tmp1.txt
t1=`cut -c18-40 tmp1.txt`
echo $t1 | sed 's/[[:space:]]//g' > temp_file.txt
cat tst.txt | sed 's/-----//g' >> ForFame/${Mn}.${dt}.txt
done
done
done

bash: put list files into a variable and but size of array is 1

I am listing the files in a directory and looping through them okay, BUT I need to know how many there are too. ${#dirlist[#]} is always 1, but for loop works?
#!/bin/bash
prefix="xxx"; # as example
len=${#prefix}; # string length
dirlist=`ls ${prefix}*.text`;
qty=${#dirlist[#]}; # sizeof array is always 1
for filelist in $dirlist
do
substring="${filelist:$len:-5}";
echo "${substring}/${qty}";
done
I have files xxx001.text upto xxx013.text
but all I get is 001/1 002/1 003/1
This:
dirlist=`ls ${prefix}*.text`
doesn't make an array. It only makes a string with space separated file names.
You have to do
dirlist=(`ls ${prefix}*.text`)
to make it an array.
Then $dirlist will reference only the first element, so you have to use
${dirlist[*]}
to reference all of them in the loop.
Declare an array of files:
arr=(~/myDir/*)
Iterate through the array using a counter:
for ((i=0; i < ${#arr[#]}; i++)); do
# [do something to each element of array]
echo "${arr[$i]}"
done
You're not creating an array unless you surround it with ( ):
dirlist=(`ls ${prefix}*.text`)
dir=/tmp
file_count=`ls -B "$dir" | wc -l`
echo File count: $file_count
The array syntax in bash is simple, using parentheses ( and ):
# string
var=name
# NOT array of 3 elements
# delimiter is space ' ' not ,
arr=(one,two,three)
echo ${#arr[#]}
1
# with space
arr=(one two three)
# or ' ',
arr=(one, two, three)
echo ${#arr[#]}
3
# brace expansion works as well
# 10 elements
arr=({0..9})
echo ${#arr[#]}
10
# advanced one
curly_flags=(--{ftp,ssl,dns,http,email,fc,fmp,fr,fl,dc,domain,help});
echo ${curly_flags[#]}
--ftp --ssl --dns --http --email --fc --fmp --fr --fl --dc --domain --help
echo ${#curly_flags[#]}
12
if you want to run a command and store the output
# a string of output
arr=$(ls)
echo ${#arr[#]}
1
# wrapping with parentheses
arr=($(ls))
echo ${#arr[#]}
256
A more advanced / handy way is by using built-in bash commands mapfile or readarray and process substitution. here is is an example of using mapfile:
# read the output of ls, save it in the array name: my_arr
# -t Remove a trailing DELIM from each line read (default newline)
mapfile -t my_arr < <(ls)
echo ${#my_arr[#]}
256

Resources