Why my bash script does not work properly? - arrays

I wrote the following bash script for resizing an image (icon.png) using file config.xml, which contains necessary image sizes and links for saving resized files.
#!/usr/bin/env bash
function help {
echo "For running the program, please write"
echo "1) config.xml file's location containing image sizes and links for saving resized images;"
echo "2) a name of an image to be resized."
}
function resize {
array=()
while read -r line
do
case $line in *icon*) array+=("$line");; esac
done < $1
for ((a=0; a < ${#array[*]}; a++))
do
echo "$a: ${array[$a]}"
imagesize=$(echo "$a: ${array[$a]}" | grep "<icon"| grep -E -o "height=\"(\d+)" | grep -E -o "\d+")
imagelink=$(echo "$a: ${array[$a]}" | grep "<icon"| grep -E -o "[^\"\'=[:space:]]+\.(jpe?g|png|gif)")
echo $2 -resize $imagesizex$imagesize! ../../$imagelink
done
echo "Resizement has been executed"
}
if [ ! -f "$1" ] || [ ! -f "$2" ]
then
help
else
resize
fi
I get the following error: "line 9: $1: ambiguous redirect"
If I check the array, I get the following output:
<icon height="36" width="36" density="ldpi" src="res/icon/android/ldpi.png"/>.
I get only one line instead of a list of lines.
Could you please help me to make the code work correctly? Also, I would like to know why the script isn't correct.

Related

How to iterate a file filled with file and directory paths to check what they are

I have a problem which is iterating a file called for example: fileAndFolderPaths, and in other script I have to iterate this same file and check if each line is a file or folder path.
fileAndFolderPaths
/opt/sampleFolder
/opt/sampleFolder/aText.txt
/opt/otherFolder
Then my script file is something like that:
myScript.sh
#!/bin/bash
mapfile -t array < /tmp/fileAndFolderPaths
function checkIfFilesOrFolder(){
for i in "${array[#]}" do
if [ -f $i ]; then
echo -e "[Info] found the file: $i"
elif [ -d $i ]; then
echo -e "[Info] found the directory: $i"
else
echo -e "[Error] Nor directory or file were found based on this value: $i"
fi
done
}
checkIfFilesOrFolder
exit 0;
The problem is the check only works for the last line of the array created by the mapfile command. Any thoughts about that? I'm new to shell scripting so probably this is a really basic problem, but even so I wasn't able to fix it yet.
A couple of review suggestions, if you don't mind:
Don't need the global variable: pass the filename to the function and loop over the file:
checkIfFilesOrFolder() {
local file=$1
while IFS= read -r line; do
# test "$line" here ...
done < "$file"
}
checkIfFilesOrFolder /tmp/fileAndFolderPaths
I recommend using local for function variables, to minimize polluting the global namespace.
Always quote your variables, unless you're aware of exactly what expansions occur on them unqoted:
if [ -f "$line" ]; then ...
is there a reason you're using echo -e? The common advice is to use
printf '[Info] found the file: %s\n' "$line"
Interesting reading: Why is printf better than
echo?

Search for a substring within listed files with spaces from multiple directories in bash

I want to create a script that loops over multiple directories from an array and, if the files there, which are not in the blacklist, are older than a certain time period, remove them. The problem is that any type of string comparison (whether grep -q or wildcards) doesn't work when trying to list a directory with files that contain spaces in them (so I change the $IFS value to loop through them), making the script unusable. Blacklisted strings can also have spaces in them, of course.
Here's what I wrote so far:
#!/bin/bash
declare -a dirs=(~/path/to/dir1/* ~/path/to/dir2/*)
declare -a blacklist=("file number 1" "file number 2" "file number 3")
saveifs=$IFS
IFS=$'\n'
echo "Starting the autocleaner..."
for dirname in "${dirs[#]}"; do
for filename in $(ls "$dirname"); do
for excluded in ${blacklist[#]}; do
if [ -e $filename ]; then
if echo "$filename" | grep -q "$excluded"; then
# if [[ "$filename" == *"$excluded"* ]]; then
:
else
if test `find "$filename" -mtime +1`; then
# rm -f $filename
echo "File $filename removed."
else
echo "File $filename is up-to-date and doesn't need to be removed."
fi
fi
else
:
fi
done
done
done
IFS=$saveifs
How can I make the comparison actually work?
Have you tried using single square brackets [ ... ] for the comparison line? Reading about the difference here between [ ... ] and [[ ... ]] may help you.

Applescript: Using Bash to find missing file from sequenced files

I found here, a code for Bash to be able to find a missing file, and this code works great because I wont be able to know the length of the sequenced files, so this is able to find the missing file without requiring me to input the last number in the sequence.
This is the code:
shopt -s extglob
shopt -s nullglob
arr=( +([0-9]).#(psd) )
for (( i=10#${arr[0]%.*}; i<=10#${arr[-1]%.*}; i++ )); do
printf -v f "%05d" $i;
[[ ! -f "$(echo "$f".*)" ]] && echo "$f is missing"
done
And it works in both terminal and iTerm.
BUT, when used in my Applescript it always reply with file 00000 is missing, when it is not:
set AEPname to "AO-M1P8"
set RENDERfolder to quoted form of POSIX path of "Volumes:RAID STORAGE:CACHES:RENDERS:AE"
set ISAEDONE to do shell script "cd /Volumes/RAID\\ STORAGE/CACHES/RENDERS/AE/AO-M1P8/
#!/bin/bash
shopt -s extglob
shopt -s nullglob
arr=( +([0-9]).#(psd) )
for (( i=10#${arr[0]%.*}; i<=10#${arr[-1]%.*}; i++ )); do
printf -v f \"%05d\" $i;
[[ ! -f \"$(echo \"$f\".*)\" ]] && echo \"$f is missing\"
done
"
display dialog ISAEDONE as text
(*
if ISAEDONE contains "is missing" then
display dialog "FILE IS MISSING"
else
display dialog "ALL FINE"
end if
*)
What I am doing wrong or is there an easier way to accomplish this?
Thanks in advance.
Screenshot
UPDATE
Seems like the way I was doing it, makes the shell unable to get the directory of the files, If I do manually input the directory, seems like it should work, but now I am getting a new kind of error:
sh: line 6: arr: bad array subscript
sh: line 6: arr: bad array subscript
Strange since I don't get this error when manually pasting the code into terminal.
I updated the code.

Using loop to convert multiple files into separate files

I used this command to convert multiple pcap log files to text using tcpdump :
$ cat /home/dalya/snort-2.9.9.0/snort_logs/snort.log.* | tcpdump -n -r - > /home/dalya/snort-2.9.9.0/snort_logs2/bigfile.txt
and it worked well.
Now I want to separate the output, each converted file in a separate output file using loop like this :
for f in /home/dalya/snort-2.9.9.0/snort_logs/snort.log.* ; do
tcpdump -n -r "$f" > /home/dalya/snort-2.9.9.0/snort_logs2/"$f.txt" ;
done
But it gave me :
bash: /home/dalya/snort-2.9.9.0/snort_logs2//home/dalya/snort-2.9.9.0/snort_logs/snort.log.1485894664.txt: No such file or directory
bash: /home/dalya/snort-2.9.9.0/snort_logs2//home/dalya/snort-2.9.9.0/snort_logs/snort.log.1485894770.txt: No such file or directory
bash: /home/dalya/snort-2.9.9.0/snort_logs2//home/dalya/snort-2.9.9.0/snort_logs/snort.log.1487346947.txt: No such file or directory
I think the problem in $f, Where did I go wrong?
If you run
for f in /home/dalya/snort-2.9.9.0/snort_logs/snort.log.* ; do
echo $f
done
You'll find that you're getting
/home/dalya/snort-2.9.9.0/snort_logs/snort.log.1485894664
/home/dalya/snort-2.9.9.0/snort_logs/snort.log.1485894770
/home/dalya/snort-2.9.9.0/snort_logs/snort.log.1487346947
You can use basename
To get only the filename, something like this:
for f in /home/dalya/snort-2.9.9.0/snort_logs/snort.log.* ; do
base="$(basename $f)"
echo $base
done
Once you're satisfied that this is working, remove the echo statement and use
tcpdump -n -r "$f" > /home/dalya/snort-2.9.9.0/snort_logs2/"$base.txt"
instead.
Edit: tcpdump -n -r "$base" > ... should have been tcpdump -n -r "$f" > ...; you only want to use $base in the context of creating the new filename, not in the context of reading the existing data.

Unable to add element to array in bash

I have the following problem. Let´s assume that $# contains only valid files. Variable file contains the name of the current file (the file I'm currently "on"). Then variable element contains data in the format file:function.
Now, when variable element is not empty, it should be put into the array. And that's the problem. If I echo element, it contains exactly what I want, although it is not stored in array, so for cycle doesn't print out anything.
I have written two ways I try to insert element into array, but neither works. Can you tell me, What am I doing wrong, please?
I'm using Linux Mint 16.
#!/bin/bash
nm $# | while read line
do
pattern="`echo \"$line\" | sed -n \"s/^\(.*\):$/\1/p\"`"
if [ -n "$pattern" ]; then
file="$pattern"
fi
element="`echo \"$line\" | sed -n \"s/^U \([0-9a-zA-Z_]*\).*/$file:\1/p\"`"
if [ -n "$element" ]; then
array+=("$element")
#array[$[${#array[#]}+1]]="$element"
echo element - "$element"
fi
done
for j in "${array[#]}"
do
echo "$j"
done
Your problem is that the while loop runs in a subshell because it is the second command in a pipeline, so any changes made in that loop are not available after the loop exits.
You have a few options. I often use { and } for command grouping:
nm "$#" |
{
while read line
do
…
done
for j in "${array[#]}"
do
echo "$j"
done
}
In bash, you can also use process substitution:
while read line
do
…
done < <(nm "$#")
Also, it is better to use $(…) in place of back-quotes `…` (and not just because it is hard work getting back quotes into markdown text!).
Your line:
element="`echo \"$line\" | sed -n \"s/^U \([0-9a-zA-Z_]*\).*/$file:\1/p\"`"
could be written:
element="$(echo "$line" | sed -n "s/^U \([0-9a-zA-Z_]*\).*/$file:\1/p")"
or even:
element=$(echo "$line" | sed -n "s/^U \([0-9a-zA-Z_]*\).*/$file:\1/p")
It really helps when you need them nested. For example, to list the lib directory adjacent to where gcc is found:
ls -l $(dirname $(dirname $(which gcc)))/lib
vs
ls -l `dirname \`dirname \\\`which gcc\\\`\``/lib
I know which I find easier!

Resources