abc.txt as
* This
Data1
Dat2
* Comment
data2
data3
When $line gets * as first char it does not work. It echoes garbage.
I tried various way but couldn't make it to work, how to check '*' in ksh varaible
while read line
do
fchar=`echo "$line" | cut -c 1-1`
#echo $fchar
if [[ ${char} = "*" ]]; then
continue
fi
if [[ ${char} = "#" ]]; then
continue
fi
echo ${line} >> stuff.txt
done<$abc.txt
Thanks.
Needs more quotes.
echo "$line" >>stuff.txt
...not
echo $line >>stuff.txt
...as the latter string-splits and glob-expands contents of the line, so it replaces * with a list of files in the current directory, replaces foo[bar] with foob if a file by that name exists, etc.
By the way, you'd be better off opening stuff.txt only once, not re-opening it every single time you want to add a new line. That'd look like this:
while read; do
fchar=${REPLY:0:1}
[[ "$fchar" = "*" ]] && continue
[[ "$fchar" = "#" ]] && continue
printf '%s\n' "$REPLY"
done >stuff.txt <abc.txt
By the way, you could also do this trivially with grep:
grep -E -v '^[*#]' <abc.txt >stuff.txt
Related
I'm taking a C++ course that requires a student header at the top of every submitted file. Typing it or yank/paste-ing is so tedious, I've been working on a script to just add it for me. I've got something that works for me so far, but I just noticed that every \n in any string in the files that it operates on are replaced with an actual newline. I'm guessing this is either a result of the use of cat or echo in the script, and I'm trying to figure out how to avoid that.
The manpage for echo says that the default behavior is to ignore backslash escapes, but I'm not entirely sure how that relates to what I'm trying to do.
My script is:
#!/bin/bash
NAME="Joseph Morgan"
CLASS="CISP 400 MoWe 3:00pm"
ASSIGNMENT=$1
DATE=$(date -I)
if [[ $# -eq 0 ]] ; then
echo 'Argument required:'
echo 'Usage: SOME_PROJECT_NAME [Adds project name to header] | d [Deletes header]'
exit 0
fi
if [ $1 == "d" ] ; then
echo 'Deleting header - Be careful! If no header is present, the first five lines of your files will be deleted'
read -p "Are you sure? (y/n)" -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]
then
for f in ./*.cpp ;
do
echo "$(tail -n +6 $f)" > $f
done
for f in ./*.h ;
do
echo "$(tail -n +6 $f)" > $f
done
fi
exit 0
fi
for f in ./*.cpp ;
do
echo -e "// $NAME\n// $CLASS\n// $ASSIGNMENT\n// $DATE\n\n$(cat $f)" > $f
done
for f in ./*.h ;
do
echo -e "// $NAME\n// $CLASS\n// $ASSIGNMENT\n// $DATE\n\n$(cat $f)" > $f
done
If there is another way entirely to accomplish this, feel free to suggest it. I'm much more interested in learning here, the script was just for fun/education so it's not incredibly important.
Thanks!
The problem is with -e parameter given to echo(1) command, which makes it interpret \n as a new line. Delete that -e and it will work. I mean, you need that -e, when you write your own headers, but you should move the $(cat $f) outside of the "echo -e". For example, in two lines:
echo -e "// $NAME\n// $CLASS\n// $ASSIGNMENT\n// $DATE\n\n" > $f
echo "$(cat $f)" >> $f # notice the double angle >>
BUT BEWARE, that would erase your file before reading it. Even here there is a problem:
echo "$(tail -n +6 $f)" > $f
because it could erase (empty) the "$f" file before reading it. You could do instead:
newcontent=$(tail -n +6 $f)
echo "$newcontent" > $f
So, to add your headers, use two distinct echoes, but read the file before writing in it:
newcontent="$(cat $f)"
echo -e "// $NAME\n// $CLASS\n// $ASSIGNMENT\n// $DATE\n\n" > $f
echo "$newcontent" >> $f # notice the double angle >>
I hope it helps.
I have a bunch of images that I need to rename, so I can use them and I was wondering how to do this.
The way they need to be is that first 5 will be kept and then for the 6th I would write a number from 1-3. I only know that the first 5 are static; on pics belonging to same "family" and can be used for comparison and the 6th char is not known.
Example:
12345random.jpg
12345randomer.jpg
0987654more_random.jpg
09876awesome.jpg
09876awesomer.jpg
09876awesomest.jpg
09876soawesomegalaxiesexplode.jpg
would become.
12345.jpg
123452.jpg
09876.jpg
098761.jpg
098762.jpg
It would be cool if it would only handle the loop so that 3 pics could be only renamed and rest skipped.
I found some stuff on removing letters to certain point, but nothing that use, since I am quite poor at bash scripting.
Here is my approach, but it kind of sucks, since I tried modifying scripts I found, but the idea is there
//I could not figure how to remove the chars after 5th not the other way around
for file in .....*; do echo mv $file `echo $file | cut -c6-`; done
done
//problem also is that once the names conflict it produces only 1 file named 12345.jpg 2nd one will not be created
//do not know how to read file names to array
name=somefile
if [[ -e $name.jpg]] ; then
i=0
while [[ -e $name-$i.jpg]] ; do
let i++
done
name=$name-$i
fi
touch $name.jpg
You can have:
new_file=${file%%[^0-9]*.jpg}.jpg
As a concept you can have this to rename files:
for file in *.jpg; do
[[ $file == [0-9]*[^0-9]*.jpg ]] || continue ## Just a simple check.
new_file=${file%%[^0-9]*.jpg}.jpg
[[ -e $new_file ]] || continue ## Do not overwrite. Delete line if not wanted.
echo "Renaming $file to $new_file." ## Optional message.
mv -- "$file" "$new_file" || echo "Failed to rename $file to $new_file."
done
If you're going to process files that also contain directory names, you'll need some more changes:
for file in /path/to/other/dirs/*.jpg *.jpg; do
base=${file##*/}
[[ $base == [0-9]*[^0-9]*.jpg ]] || continue
if [[ $file == */* ]]; then
new_file=${file%/*}/${base%%[^0-9]*.jpg}.jpg
else
new_file=${file%%[^0-9]*.jpg}.jpg
fi
[[ -e $new_file ]] || continue
echo "Renaming $file to $new_file."
mv -- "$file" "$new_file"
done
you can also try the following code
but be careful all the files should be in .jpg format and pass the name of folder as an argument
#!/bin/bash
a=`ls $1`
for b in $a
do
echo $b
if (( i<4 ))
then
c=`echo $b | cut -c1-5`
let i=i+1
c="$c$i.jpg"
echo $c
else
c=`echo $b | cut -c1-5`
c="$c.jpg"
break
fi
mv $1$b $1$c
done
Can anyone see whats wrong here? If I put X|9 in lan.db (or any db in this directory) and run the following code, the IF statement does not work. It's weird! if you echo $LINE, it is indeed pulling X|9 out of lan.db (or any db in this directory) and setting it equal to LINE, but it wont do the comparison.
DBREGEX="^[0-9]|[0-9]$"
shopt -s nullglob
DBARRAY=(databases/*)
i=0
for i in "${!DBARRAY[#]}"; do
cat ${DBARRAY[$i]} | grep -v \# | while read LINE; do
echo "$LINE" (Whats weird is that LINE DOES contain X|9)
if [[ !( $LINE =~ $DBREGEX ) ]]; then echo "FAIL"; fi
done
done
If however I just manually sent LINE="X|9" the same code (minus the while) works fine. ie LINE=X|9 fails, but LINE=9|9 succeeds.
DBREGEX="^[0-9]|[0-9]$"
Comment shopt -s nullglob
Comment DBARRAY=(databases/*)
Comment i=0
Comment for i in "${!DBARRAY[#]}"; do
Comment cat ${DBARRAY[$i]} | grep -v \# | while read LINE; do
LINE="X|9"
if [[ !( $LINE =~ $DBREGEX ) ]]; then echo "FAIL"; fi
Comment done
Comment done
* UPDATE *
UGH I GIVE UP
Now not even this is working...
DBREGEX="^[0-9]|[0-9]$"
LINE="X|9"
if [[ ! $LINE =~ $DBREGEX ]]; then echo "FAIL"; fi
* UPDATE *
Ok, so it looks like I have to escape |
DBREGEX="^[0-9]\|[0-9]$"
LINE="9|9"
echo "$LINE"
if [[ ! $LINE =~ $DBREGEX ]]; then echo "FAIL"; fi
This seems to work ok again
| has a special meaning in a regular expression. ^[0-9]|[0-9]$ means "starts with a digit, or ends with a digit". If you want to match a literal vertical bar, backslash it:
DBREGEX='^[0-9]\|[0-9]$'
for LINE in 'X|9' '9|9' ; do
echo "$LINE"
if [[ ! $LINE =~ $DBREGEX ]] ; then echo "FAIL" ; fi
done
You don't need round brackets in regex evaluation. You script is also creating a sub shell and making a useless use of cat which can be avoided.
Try this script instead:
while read LINE; do
echo "$LINE"
[[ "$LINE" =~ $DBREGEX ]] && echo "PASS" || echo "FAIL"
done < <(grep -v '#' databases/lan.db)
So I have this block of code. Basically, I'm taking file $i, checking if it's got content or not, checking if I can read it, if I can open it, grab the first line and see if it's a bash file. When I run this every time on a non-empty file, it was registers as true and echo's bash.
## File is empty or not
if [[ -s $i ]]
then
## Can we read the file
if [[ -r $i ]]
then
## File has content
if [[ $(head -n 1 $i) = "#! /bin/bash" ]]
then
echo -n " bash"
fi
fi
else
## file does not have content
echo -n " empty"
fi
This is what does the check of if it's bash:
if [[ $(head -n 1 $i) = "#! /bin/bash" ]]
Replace [[ with [ and enclose $(head -n 1 $i) in quotes.
[[ is itself an operator that tests its contents.
I have a shell script like this:
cat file | while read line
do
# run some commands using $line
done
Now I need to check if the line contains any non-whitespace character ([\n\t ]), and if not, skip it.
How can I do this?
Since read reads whitespace-delimited fields by default, a line containing only whitespace should result in the empty string being assigned to the variable, so you should be able to skip empty lines with just:
[ -z "$line" ] && continue
try this
while read line;
do
if [ "$line" != "" ]; then
# Do something here
fi
done < $SOURCE_FILE
bash:
if [[ ! $line =~ [^[:space:]] ]] ; then
continue
fi
And use done < file instead of cat file | while, unless you know why you'd use the latter.
cat i useless in this case if you are using while read loop. I am not sure if you meant you want to skip lines that is empty or if you want to skip lines that also contain at least a white space.
i=0
while read -r line
do
((i++)) # or $(echo $i+1|bc) with sh
case "$line" in
"") echo "blank line at line: $i ";;
*" "*) echo "line with blanks at $i";;
*[[:blank:]]*) echo "line with blanks at $i";;
esac
done <"file"
if ! grep -q '[^[:space:]]' ; then
continue
fi
blank=`tail -1 <file-location>`
if [ -z "$blank" ]
then
echo "end of the line is the blank line"
else
echo "their is something in last line"
fi
awk 'NF' file | while read line
do
# run some commands using $line
done
stole this answer to a similar question:
Delete empty lines using sed