Shell scripting to print list of elements - bash

Is there any command in shell scripting which is similar to "list" in tcl? I want to write a list of elements to a file (each in separate line) .But, if the element matches a particular pattern then element next to it and the element itself should be printed in the same line. Is there any command in shell script for doing this?
example: my string is like " execute the command run abcd.v"
I want to write each word in separate lines of a file but if the word is "run" then abcd.v and run must be printed in the same line. So, the output should be like,
execute
the
command
run abcd.v
How to do this in shell scripting?

line="execute the command run abcd.v"
for word in $line # the variable needs to be unquoted to get "word splitting"
do
case $word in
run|open|etc) sep=" " ;;
*) sep=$'\n' ;;
esac
printf "%s%s" $word "$sep"
done
See http://www.gnu.org/software/bash/manual/bashref.html#Word-Splitting

Here's how you can do it in bash:
Name this following script as list
Set it to executable
Copy it to your ~/bin/:
List:
#!/bin/bash
# list
while [[ -n "$1" ]]
do
if [[ "$1" == "run" ]]; then
echo "$1 $2"
else
echo "$1"
fi
shift
done
And this is how you can use it on the command prompt:
list execute the command run abcd.v > outputfile.txt
And your outputfile.txt will be written as:
execute
the
command
run abcd.v

You could accomplish it by using the below script. It would not be a single command. Below is a for loop that has an if statment to check for the keyword run. It does not append a new line character( echo -n ).
for i in `echo "execute the command run abcd.v"`
do
if [ $i = "run" ] ; then
echo -n "$i " >> fileOutput
else
echo $i >> fileOutput
fi
done

Related

Use key/value data from a file in a shell script

I have a file called Year.txt
Year2000= 1/2/3/4/
Year2001= 5/6/7/8/
Year2002= 9/10/11/12/
....
....
....
Year2020= 100/101/102/
etc and so on
I need to take this Year.txt as reference in my another script some sample.sh
sample.sh
source /home/user/Year.txt
d=cp $filename $1
echo $d
sample.sh Year2000(passing Year2000 as first argument)
**I need to cut the second part after = if I pass Year2000 as my argument and paste this 1/2/3/4/ in my statement
**I need to cut the second part after = if I pass Year2001 as my argument and paste this 5/6/7/8/ in my copy statement
etc..
I need output like this:
Input1 sample.sh Year2000
Output: cp somefile.txt 1/2/3/4/
Input2: sample.sh Year2001
Output: cp somefile.txt 5/6/7/8/
In Short -- I need to take the reference from another file and generate the copy statement
Don't source files that aren't legal bash code. In this case, an associative array lets you store as many key/value pairs as you need inside a single variable.
#!/usr/bin/env bash
case $BASH_VERSION in ''|[123].*) echo "ERROR: Needs bash 4.0 or newer" >&2; exit 1;; esac
year_name=$1
file_name=$2
[[ $file_name ]] || { echo "Usage: $0 year-name file-name" >&2; exit 1; }
# Read year.txt, and generate a map
declare -A dirs_by_year=( )
while IFS='= ' read -r k v; do
dirs_by_year[$k]=$v
done <Year.txt
if ! [[ ${dirs_by_year[$year_name]} ]]; then
echo "ERROR: User specified year $1, but input file does not have a directory for it" >&2
echo " ...defined years follow:" >&2
declare -p dirs_by_year >&2 # print array definition to show what we read
exit 1
fi
# generate and write a cp command
printf '%q ' cp "$file_name" "${dirs_by_year[$year_name]}"

How to make multiple search and replace in files via bash

i have script. In this script i made search and replace of words. Word by word until word 'end'. It is ok and it works. You can see body of my script:
#!/bin/bash
end=end
until [ "$first" = "$end" ];do
echo "please write first word";
read first
if grep -q "$first" *txt; then
echo "word is exists"
grep "$first" *txt
echo "please write second word";
read second
sed -i 's/'"$first"'/'"$second"'/g' *txt
else
echo "second word does not exists"
exit 1
fi
done
It works for me. I have in the result console, where I can endlessly loop words, but if i want to do something like this: How can i write multiple words in line.
For example: "dog" "cat" "fish"
And search and replace all of these words. How can do it? For example, if i need to replace on these words ("elephat" "mouse" "bird"). How can you do it?
I mean search and replace words, like arguments.
You just need a loop to process the arguments.
Assuming you run the script passing pairs of original replacement words (myscript.sh original_word1 replacement1 original_word2 replacement2 ...) it would be something like the following:
while [[ $# -gt 1 ]]
do
original="$1"
replacement="$2"
# your code for actually replacing $original with $replacement
shift # discard already processed original arg
shift # discard already processed replacement arg
done
Note that if the user passes a last original word without replacement the script will just ignore it
Your English is rough, but I think you want to be able to prompt for multiple words, and replace them with a new set?
The below code will let you run a program like replace_words one two three and then be prompted for a list of words to replace, e.g. 1 2 3. After that, it exits.
declare -a replace_list=( "$#" ) # get the replace list as passed arguments
echo -n "Enter words to replace with: "; read -ra sub_list
for ((i=0; i < "${#replace_list[#]}"; ++i)); do
if grep -q "${replace_list[$i]}" *txt; then
echo "first word is exists"
sed -i "s/${replace_list[$i]}/${sub_list[$i]}/g" *txt
else
echo "${replace_list[$i]} does not exists"
exit 1
fi
done

How to loop script till user input is empty?

I am trying to make my script to repeat till the user leaves the block question empty. I just got the loop to run, but I can not find a way to make it possible to stop it when block is empty.
I hope some one can help me!!
#!/bin/tcsh -f
#
set word="start"
until ($word !=""); do
#First ask for Compound and Block Name.
echo -n "please enter block name: "
read block
echo -n "please enter compound name: "
read compound
#Now coping template with new name
#
cp Template $block
#
for line in `cat $block`;do
echo $line | sed -e "s/test1/${block}/g" -e "s/test2/${compound}/g" >>./tmp124.txt
done
mv ./tmp124.txt $block
done
Do you want to use bash or csh? You are using bash syntax but tagged your question csh and call tcsh in the first line of your code.
To answer your question, here are examples of how to iterate on standard input until some input is empty:
For tcsh:
#!/bin/tcsh
while ( 1 )
set word = "$<"
if ( "$word" == "" ) then
break
endif
# rest of code...
end
For bash:
#!/bin/bash
while read word; do
if [ -z $word ]; then
break
fi
# rest of code...
done
Use "Until do" loop,
Eg :
For session variable i am assigning default value, Then entering loop. User can pass any value on each prompt when the value is empty, Loop will Terminate and exit the script.
session="Mysession"
until [$session -eq $null]
do
echo $session
echo "Leave Blank to Terminate session"
read -p "Enter session name : " session
done
echo "Exiting.."

Bash: Formatting results inside for loop from a ls command

How come the additional 'Line' insideecho "Line $line" is not prepended to all files inside the for loop?
#!/bin/bash
INPUT=targets.csv
IFS=","
[ ! -f $INPUT ] && { echo "$INPUT file not found"; exit 99; }
while read target user password path
do
result=$(sshpass -p "$password" ssh -n "$user"#"$target" ls "$path"*file* 2>/dev/null)
if [ $? -ne 0 ]
then
echo "No Heap dumps detected."
else
echo "Found a Heap dump! Possible OOM issue detected"
for line in $result
do
echo "Line $line"
done
fi
done < $INPUT
.csv file contents ..
rob#laptop:~/scripts$ cat targets.csv
server.com,root,passw0rd,/root/
script output ..
rob#laptop:~/scripts$ ./checkForHeapdump.sh
Found a Heap dump! Possible OOM issue detected
Line file1.txt
file2.txt
The statement:
for line in $result
performs word splitting on $result to get each element that $line should be set to. Word splitting uses the delimiters in $IFS. Earlier in the script you set this to just ,. So this loop will iterate over comma-separated data in $result. Since there aren't any commas in it, it's just a single element.
If you want to split it by lines, do:
IFS="
"
for line in $result

How to handle "--" in the shell script arguments?

This question has 3 parts, and each alone is easy, but combined together is not trivial (at least for me) :)
Need write a script what should take as its arguments:
one name of another command
several arguments for the command
list of files
Examples:
./my_script head -100 a.txt b.txt ./xxx/*.txt
./my_script sed -n 's/xxx/aaa/' *.txt
and so on.
Inside my script for some reason I need distinguish
what is the command
what are the arguments for the command
what are the files
so probably the most standard way write the above examples is:
./my_script head -100 -- a.txt b.txt ./xxx/*.txt
./my_script sed -n 's/xxx/aaa/' -- *.txt
Question1: Is here any better solution?
Processing in ./my_script (first attempt):
command="$1";shift
args=`echo $* | sed 's/--.*//'`
filenames=`echo $* | sed 's/.*--//'`
#... some additional processing ...
"$command" "$args" $filenames #execute the command with args and files
This solution will fail when the filenames will contain spaces and/or '--', e.g.
/some--path/to/more/idiotic file name.txt
Question2: How properly get $command its $args and $filenames for the later execution?
Question3: - how to achieve the following style of execution?
echo $filenames | $command $args #but want one filename = one line (like ls -1)
Is here nice shell solution, or need to use for example perl?
First of all, it sounds like you're trying to write a script that takes a command and a list of filenames and runs the command on each filename in turn. This can be done in one line in bash:
$ for file in a.txt b.txt ./xxx/*.txt;do head -100 "$file";done
$ for file in *.txt; do sed -n 's/xxx/aaa/' "$file";done
However, maybe I've misinterpreted your intent so let me answer your questions individually.
Instead of using "--" (which already has a different meaning), the following syntax feels more natural to me:
./my_script -c "head -100" a.txt b.txt ./xxx/*.txt
./my_script -c "sed -n 's/xxx/aaa/'" *.txt
To extract the arguments in bash, use getopts:
SCRIPT=$0
while getopts "c:" opt; do
case $opt in
c)
command=$OPTARG
;;
esac
done
shift $((OPTIND-1))
if [ -z "$command" ] || [ -z "$*" ]; then
echo "Usage: $SCRIPT -c <command> file [file..]"
exit
fi
If you want to run a command for each of the remaining arguments, it would look like this:
for target in "$#";do
eval $command \"$target\"
done
If you want to read the filenames from STDIN, it would look more like this:
while read target; do
eval $command \"$target\"
done
The $# variable, when quoted will be able to group parameters as they should be:
for parameter in "$#"
do
echo "The parameter is '$parameter'"
done
If given:
head -100 test this "File name" out
Will print
the parameter is 'head'
the parameter is '-100'
the parameter is 'test'
the parameter is 'this'
the parameter is 'File name'
the parameter is 'out'
Now, all you have to do is parse the loop out. You can use some very simple rules:
The first parameter is always the file name
The parameters that follow that start with a dash are parameters
After the "--" or once one doesn't start with a "-", the rest are all file names.
You can check to see if the first character in the parameter is a dash by using this:
if [[ "x${parameter}" == "x${parameter#-}" ]]
If you haven't seen this syntax before, it's a left filter. The # divides the two parts of the variable name. The first part is the name of the variable, and the second is the glob filter (not regular expression) to cut off. In this case, it's a single dash. As long as this statement isn't true, you know you have a parameter. BTW, the x may or may not be needed in this case. When you run a test, and you have a string with a dash in it, the test might mistake it for a parameter of the test and not the value.
Put it together would be something like this:
parameterFlag=""
for parameter in "$#" #Quotes are important!
do
if [[ "x${parameter}" == "x${parameter#-}" ]]
then
parameterFlag="Tripped!"
fi
if [[ "x${parameter}" == "x--" ]]
then
print "Parameter \"$parameter\" ends the parameter list"
parameterFlag="TRIPPED!"
fi
if [ -n $parameterFlag ]
then
print "\"$parameter\" is a file"
else
echo "The parameter \"$parameter\" is a parameter"
fi
done
Question 1
I don't think so, at least not if you need to do this for arbitrary commands.
Question 3
command=$1
shift
while [ $1 != '--' ]; do
args="$args $1"
shift
done
shift
while [ -n "$1" ]; do
echo "$1"
shift
done | $command $args
Question 2
How does that differ from question 3?

Resources