basic bash syntax issue - syntax error near unexpected token - linux

I think my script does what its meant to do, at first I just had
#!/bin/bash
for file in /home/parallels/Desktop/trashcan/*
do
echo "Would you like to delete - " $file
done
I then wanted to add the obvious missing functionality so I now have
#!/bin/bash
for file in /home/parallels/Desktop/trashcan/*
do
echo "Would you like to delete - " $file
read line
if [$line == y|Y]
sudo rm $file
fi
done
Thats where I'm at now, I did at first try to use a case statement instead of the if as I have a working script with the case statement I'd need but simply copying it over gives me the same error - syntax error near unexpeted token, I get this for fi and done

[ is a command, so it must be separated by whitespace from its first argument:
if [ "$line" = y ] || [ "$line" = Y ]; then
sudo rm "$file"
fi
If you are using bash, you can replace the standard usage shown above with the more concise
if [[ $line = [yY] ]]; then
sudo rm "$file"
fi
As Keith Thompson pointed out, only an answer of exactly 'y' or 'Y' will allow the file to be removed. To allow 'yes' or 'Yes' as well, you can use
shopt -s extglob
if [[ $line = [yY]?(es) ]]
(The shopt line is only required for earlier versions of bash. Starting with version 4, extended patterns are the default in a conditional expression.)

The if part should be
if [ "$line" = "y" ] || [ "$line" = "Y" ]
then
sudo rm $file
fi

I faced similar problem. I had opened the .sh file in windows and Windows has added CRLF at the end of every line.
I found this out by running
cat --show-nonprinting filename.extension
E.g.
cat --show-nonprinting test.sh
Then, I converted it using
dos2unix filename.extension
E.g.
dos2unix myfile.txt
dos2unix myshell.sh

Related

Linux Script; For Loop to rename; New to Scripting

You will have to forgive me I have very little experience writing Linux Scripts. Ok What I'm trying to do is rename part of a file that has a specified name in, but the problem I'm coming across is I get the error during my For Loop is this 0403-011 The specified substitution is not valid for this command I'm not sure what I'm doing wrong in my for loop, can someone please assist?
#Creates Directory
echo "Name of New Directory"
read newdir
if [[ -n "$newdir" ]]
then
mkdir $newdir
fi
echo $userInput Directory Created
echo
echo "Directory you wish to Copy?"
read copydir
if [[ -n "$copydir" ]]
then
#Copies contents of Specified Directory
cp -R $copydir/!(*.UNC) $newdir;
#Searches through directory
for file in $newdir/$copydir*; do
mv -v -- "$file" "${file/old/new}";
done
fi
Which version of ksh are you using?
"${file//old/new}" and "${file/old/new}" are valid syntaxes in ksh93.
If your env is ksh88 "${file//old/new}" substitution is not supported.
You have to use sed/tr to replace pattern. Here is an example with sed.
mv -v -- "$file" "$(echo ${file}|sed 's/old/new/')"
The offending line:
mv -v -- "$file" "${file/old/new}";
should be:
mv -v -- "$file" "${file//old/new}";
If you want to replace $old with $new (as opposed to the literal string "old" with "new"), write:
mv -v -- "$file" "${file//$old/$new}";

How can I keep file name to variable from this code

readLBL.sh
#!/bin/bash
while IFS='' read -r line || [[ -n "$line" ]]; do
echo "Text read from file: $line"
done < "$1"
When I run this shell script in Terminal and I have to insert file name for run it
Example :
./readLBL.sh science.txt
output :
58050364;Tom Jones
58050365;Marry Jane
how can i keep "science.txt" into some variable like a = "science.txt"
Use $1 as this is the argument you are already using to read the file in the first instance.
#!/bin/bash
while IFS='' read -r line || [[ -n "$line" ]]; do
echo "Text read from file $1: $line"
done < "$1"
Running this gives the following:
$ ./readLBL.sh science.txt
Text read from file science.txt: 58050364;Tom Jones
Text read from file science.txt: 58050365;Marry Jane
a="science.txt"
from the Linux command line, will set $a equal to science.txt
If you want this permanent, you could perhaps add it to the bottom of one of your linux profiles. For example add it to the bottom of ~/.bashrc

Creating a pathname to check a file doesn't exist there / Permission denied error

Hello from a Linux Bash newbie!
I have a list.txt containing a list of files which I want to copy to a destination($2). These are unique images but some of them have the same filename.
My plan is to loop through each line in the text file, with the copy to the destination occurring when the file is not there, and a mv rename happening when it is present.
The problem I am having is creating the pathname to check the file against. In the code below, I am taking the filename only from the pathname, and I want to add that to the destination ($2) with the "/" in between to check the file against.
When I run the program below I get "Permission Denied" at line 9 which is where I try and create the path.
for line in $(cat list.txt)
do
file=$[ basename $line ]
path=$[ $2$file ]
echo $path
if [ ! -f $path ];
then
echo cp $line $2
else
echo mv $line.DUPLICATE $2
fi
done
I am new to this so appreciate I may be missing something obvious but if anyone can offer any advice it would be much appreciated!
Submitting this since OP is new in BASH scripting no good answer has been posted yet.
DESTINATION="$2"
while read -r line; do
file="${line##*/}"
path="$2/$file"
[[ ! -f $path ]] && cp "$line" "$path" || mv "$line" "$path.DUP"
done < list.txt
Don't have logic for counting duplicates at present to keep things simple. (Which means code will take care of one dup entry) As an alternative you get uniq from list.txt beforehand to avoid the duplicate situation.
#anubhava: Your script looks good. Here is a small addition to it to work with several dupes.
It adds a numer to the $path.DUP name
UniqueMove()
{
COUNT=0
while [ -f "$1" ]
do
(( COUNT++ ))
mv -n "$1" "$2$COUNT"
done
}
while read -r line; do
file="${line##*/}"
path="$2/$file"
[[ ! -f $path ]] && cp "$line" "$path" || UniqueMove "$line" "$path.DUP"
done < list.txt

Bash: Create a file if it does not exist, otherwise check to see if it is writeable

I have a bash program that will write to an output file. This file may or may not exist, but the script must check permissions and fail early. I can't find an elegant way to make this happen. Here's what I have tried.
set +e
touch $file
set -e
if [ $? -ne 0 ]; then exit;fi
I keep set -e on for this script so it fails if there is ever an error on any line. Is there an easier way to do the above script?
Why complicate things?
file=exists_and_writeable
if [ ! -e "$file" ] ; then
touch "$file"
fi
if [ ! -w "$file" ] ; then
echo cannot write to $file
exit 1
fi
Or, more concisely,
( [ -e "$file" ] || touch "$file" ) && [ ! -w "$file" ] && echo cannot write to $file && exit 1
Rather than check $? on a different line, check the return value immediately like this:
touch file || exit
As long as your umask doesn't restrict the write bit from being set, you can just rely on the return value of touch
You can use -w to check if a file is writable (search for it in the bash man page).
if [[ ! -w $file ]]; then exit; fi
Why must the script fail early? By separating the writable test and the file open() you introduce a race condition. Instead, why not try to open (truncate/append) the file for writing, and deal with the error if it occurs? Something like:
$ echo foo > output.txt
$ if [ $? -ne 0 ]; then die("Couldn't echo foo")
As others mention, the "noclobber" option might be useful if you want to avoid overwriting existing files.
Open the file for writing. In the shell, this is done with an output redirection. You can redirect the shell's standard output by putting the redirection on the exec built-in with no argument.
set -e
exec >shell.out # exit if shell.out can't be opened
echo "This will appear in shell.out"
Make sure you haven't set the noclobber option (which is useful interactively but often unusable in scripts). Use > if you want to truncate the file if it exists, and >> if you want to append instead.
If you only want to test permissions, you can run : >foo.out to create the file (or truncate it if it exists).
If you only want some commands to write to the file, open it on some other descriptor, then redirect as needed.
set -e
exec 3>foo.out
echo "This will appear on the standard output"
echo >&3 "This will appear in foo.out"
echo "This will appear both on standard output and in foo.out" | tee /dev/fd/3
(/dev/fd is not supported everywhere; it's available at least on Linux, *BSD, Solaris and Cygwin.)

Bash string comparison syntax

I was looking through the /etc/bash_completion script found in some Debian packages. I was interested in using the code that looks through a specific directory (/etc/bash_completion.d/ by default) and sources every file in that directory.
Unfortunately, trying to run the script causes errors under the Mac OS X version of bash. The lines in question are:
for i in $BASH_COMPLETION_DIR/*; do
[[ ${i##*/} != #(*~|*.bak|*.swp|\#*\#|*.dpkg*|.rpm*) ]] &&
[ \( -f $i -o -h $i \) -a -r $i ] && . $i
done
Specifically, my version of bash (3.2.17) chokes on the #() construction. I get that the point of that first test is to make sure we don't source any editor swap files or backups, etc. I'd like to understand exactly what that #() syntax does, and, if possible how to get something similar (and similarly elegant) running on my ancient copy of bash. Can anyone offer insight?
It's just an extension to the shell comparison which is equivalent to the grep "or" operator (|).
Depending on your bash version, it may not be available or you may have to set extglob with the shopt built-in. See the following session transcript:
pax#daemonspawn> $ bash --version
GNU bash, version 3.2.48(21)-release (i686-pc-cygwin)
Copyright (C) 2007 Free Software Foundation, Inc.
pax#daemonspawn> echo #(*~|*.pl)
bash: syntax error near unexpected token '('
pax#daemonspawn> shopt extglob
extglob off
pax#daemonspawn> shopt -s extglob
pax#daemonspawn> echo #(*~|*.pl)
qq.pl qq.sh~ xx.pl
pax#daemonspawn>
That allows the following to work:
?(pattern-list)
Matches zero or one occurrence of the given patterns
*(pattern-list)
Matches zero or more occurrences of the given patterns
+(pattern-list)
Matches one or more occurrences of the given patterns
#(pattern-list)
Matches one of the given patterns
!(pattern-list)
Matches anything except one of the given patterns
If you can't get it working with shopt, you can generate a similar effect with older methods such as:
#!/bin/bash
for i in $BASH_COMPLETION_DIR/*; do
# Ignore VIM, backup, swp, files with all #'s and install package files.
# I think that's the right meaning for the '\#*\#' string.
# I don't know for sure what it's meant to match otherwise.
echo $i | egrep '~$|\.bak$|\.swp$|^#*#$|\.dpkg|\.rpm' >/dev/null 2>&1
if [[ $? == 0 ]] ; then
. $i
fi
done
Alternatively, if there's multiple complex determinations that will decide whether you want it sourced, you can use a doit variable that's initially set to true, and set it to false if any of those conditions trigger. For example, the following script qq.sh:
#!/bin/bash
for i in * ; do
doit=1
# Ignore VIM backups.
echo $i | egrep '~$' >/dev/null 2>&1
if [[ $? -eq 0 ]] ; then
doit=0
fi
# Ignore Perl files.
echo $i | egrep '\.pl$' >/dev/null 2>&1
if [[ $? -eq 0 ]] ; then
doit=0
fi
if [[ ${doit} -eq 1 ]] ; then
echo Processing $i
else
echo Ignoring $i
fi
done
did this in my home directory:
Processing Makefile
Processing binmath.c
: : : : :
Processing qq.in
Ignoring qq.pl --+
Processing qq.sh |
Ignoring qq.sh~ --+--- see?
Processing qqin |
: : : : : |
Ignoring xx.pl --+

Resources