So I'm fairly new to Shell scripting and trying to build a function that deletes a line from a .txt file.
To be clear I want to be able to run the following command
$ ./script.sh searchTerm delete
Which should find the line containing 'searchTerm' and remove it.
I am passing the $1 (to capture the searchTerm) into the deletePassword function but can't seem to get it to work.
Would love some advice :)
#Delete a password
if [[ $2 == "delete" ]]; then
deletePassword $1
fi
function deletePassword () {
line=grep -Hrn $1 pwstore.txt
sed -n $line pwstore.txt
echo "Deleted that for you.."
}
When running the previous command I get the following error:
sed: 1: "pwstore.txt": extra characters at the end of p command
Your line variable isn't being set as you expect, as you need to use command substitution to capture the result of a command like that. eg:
line=$(grep -Hrn $1 pwstore.txt)
I would suggest just using sed instead:
sed -i.bak "/$1/d" pwstore.txt
This will delete any lines which match the string stored in $1 from pwstore.txt (and create a backup of the original file at pwstore.txt.bak)
Related
I have a frequent situation where I want to run a given command over all files that match a certain pattern. As such I have the following:
iterate.sh
#!/bin/bash
for file in $1; do
$2
done
So I can use it as such iterate.sh "*.png" "echo $file"
The problem is when the command is being ran it doesn't seem to have access to the $file variable as that sample command will just output a blank line for every file.
How can I reference the iterator $file from the arguments of the program?
#!/bin/bash
for file in $1; do
eval $2
done
iterate.sh "*.png" 'echo $file'
Need single quotes around the argument with $file so it doesn't expand on the command line. Need to eval in the loop to actually do the command in the argument instead of just expanding the argument.
How would I write a bash script that parses a text file, finding any lines that contain the word command: and then saves the entirety of each line on which it was found on to a text file?
The command would be
grep command: your_filename >> save_filename
Which is
#!/bin/bash
grep command: $1 >> $2
Executed by
scriptname your_filename save_filename
Thanks David
Note that I'm using an appender >>, instead of a create >. The latter ensures a file with only your last run in it, whereas the appender will add new lines to the file if it already exists.
If you are looking for a pure bash solution of this grep-like behaviour:
#!/bin/bash
# Usage: ./mygrep ERE_PATTERN FILENAME
while IFS= read -r line || [[ $line ]]; do
[[ $line =~ $1 ]] && echo "$line"
done <"$2"
(We iterate over lines of the file given in the second positional parameter, $2, in a pretty standard way, checking for a match with a pattern given as the first parameter inside a conditional expression with the =~ operator, printing all lines that match.)
Invoke it like:
./mygrep command: file
Although much slower than grep, one nice thing about this script is that it supports POSIX ERE (extended regular expressions) by default (you don't need to specify -E like you do in grep), e.g.:
./mygrep 'com.*:' file
./mygrep '^[[:digit:]]{3}' file
# etc
I'm starting to learn how to write shell scripts. I have them all placed in a folder 'personal-scripts' in my home directory. They are starting to add up though. To solve this, I am attempting to create a script that loops over the directory and gives me a brief sentence about what each script does.
For now I can only output the script location and names via:
scriptsinfo
#!/bin/bash
for filename in ~/personal-scripts/*
do
echo $filename
done
Since I don't want to go back and update this file manually, I want to place the about sentence inside each other script either as a comment or string variable that can be evaluated.
How can I read the contents of each other script in the folder and output their string/info about what they do in this script?
You can do that using head command, which prints the first n lines of a file.
test.sh
# this is about line
# date is 14-9-2017
script data
..
..
~# head -n 2 test.sh
# this is about line
# date is 14-9-2017
If you add the description on each second line of your script, (after #!/bin/bash then let use sed -n "2p" $filenamein your script. You can also add a separator between each script:
#!/bin/bash
for filename in ~/personal-scripts/*
do
echo "---------------------"
echo $filename
echo "---------------------"
sed -n "2p" $filename
done
The alternative is to put the description anywhere, in a line starting by e.g # about: then you can grep it:
#!/bin/bash
for filename in ~/personal-scripts/*
do
echo "---------------------"
echo $filename
echo "---------------------"
grep "# about:" $filename | sed 's/# about://'
done
The | sed 's/# about://' is there to keep the description only.
If you have a comment inside all your scripts with a specific pattern such as
#info : This script runs daily
Then you can simply grep for the pattern and append to the name of the script in each line.
for filename in ~/personal-scripts/*
do
echo "$i : $(grep '#info' $i)"
done
I have written a script to change file ownerships based on an input list read in. My script works fine on directories without space in their name. However it fails to change files on directories with space in their name. I also would like to capture the output from the chown command to a file. Could anyone help ?
here is my script in ksh:
#!/usr/bin/ksh
newowner=eg27395
dirname=/home/sas/sastest/
logfile=chowner.log
date > $dir$logfile
command="chown $newowner:$newowner"
for fname in list
do
in="$dirname/$fname"
if [[ -e $in ]]
then
while read line
do
tmp=$(print "$line"|awk '{if (substr($2,1,1) == "/" ) print $2; if (substr($0,1,1) == "/" ) print '})
if [[ -e $tmp ]]
then
eval $command \"$tmp\"
fi
done < $in
else
echo "input file $fname is not present. Check file location in the script."
fi
done
a couple of other errors:
date > $dir$logfile -- no $dir variable defined
to safely read from a file: while IFS= read -r line
But to answer your main concern, don't try to build up the command so dynamically: don't bother with the $command variable, don't use eval, and quote the variable.
chmod "$newowner:$newowner" "$tmp"
The eval is stripping the quotes on this line
command="chown $newowner:$newowner"
In order to get the line to work with spaces you will need to provide backslashed quotes
command="chown \"$newowner:$newowner\""
This way the command that eval actually runs is
chown "$newowner:$newowner"
Also, you probably need quotes around this variable setting, although you'll need to tweak the syntax
tmp="$(print "$line"|awk '{if (substr($2,1,1) == "/" ) print $2; if (substr($0,1,1) == "/" ) print '})"
To capture the output you can add 2>&1 > file.out where file.out is the name of the file ... in order to get it working with eval as you are using it you will need to backslash any special characters much in the same way you need to backslash the double quotes
Your example code suggests that list is a "meta" file: A list of files that each has a list of files to be changed. When you only have one file you can remove the while loop.
When list is a variable with filenames you need echo "${list}"| while ....
It is not completely clear why you sometimes want to start with the third field. It seems that sometimes you have 2 words before the filename and want them to be ignored. Cutting the string on spaces becomes a problem when your filenames have spaces as well. The solution is look for a space followed by a slash: that space is not part of a filename and everything up to that space can be deleted.
newowner=eg27395
# The slash on the end is not really part of the dir name, doesn't matter for most commands
dirname=/home/sas/sastest
logfile=chowner.log
# Add braces, quotes and change dir into dirname
date > "${dirname}/${logfile}"
# Line with command not needed
# Is list an inputfile? It is streamed using "< list" at the end of while []; do .. done
while IFS= read -r fname; do
in="${dirname}/${fname}"
# Quotes are important
if [[ -e "$in" ]]; then
# get the filenames with a sed construction, and give it to chmod with xargs
# The sed construction is made for the situation that in a line with a space followed by a slash
# the filename starts with the slash
# sed is with # to avoid escaping the slashes
# Do not redirect the output here but after the loop.
sed 's#.* /#/#' "${in}" | xargs chmod ${newowner}:${newowner}
else
echo "input file ${fname} is not present. Check file location in the script."
fi
done < list >> "${dirname}/${logfile}"
I run a command:
script.sh ___bubu__
The content of the script.sh is:
echo $1
When executed I get
___bubu__
How can I remove the trailing spaces from command line passed arguments?
I copy some params from a file and when pasting into command line I get some spaces and I do not want to manually remove the space. I am planning to use $1 as a parameter in a script. For example I want to create a folder with $1
Would something like this work?
..> more strip.sh
#!/bin/bash
arg=$1
arg=${arg// /}
echo "unstripped: --$1--"
echo "stripped: --$arg--"
.03:163> ./strip.sh " test "
unstripped: -- test --
stripped: --test--