so I am trying to make a bash script loop that takes a users file name they want and the number of files they want and creates empty files. I made the script but I keep getting the error "./dog.sh: line 6: 0]: No such file or directory". I'm new to bash script and don't know what I'm doing wrong. Any help would be awesome thanks!
#!/bin/bash
echo "what file name do you want?"; read filename
echo "how many files do you want"; read filenumber
x=$filenumber
if [$x < 0]
then
touch $fiename$filenumber
x=$((x--))
fi
for x in $(seq "$filenumber"); do touch "$filename$x"; done
seq $filenumber produces a list of numbers from 1 to $filenumber. The for loop assigns x to each of these numbers in turn. The touch command is run for each value of x.
Alternative
In bash, if we can type the correct file number into the command line, the same thing can be accomplished without a for loop:
$ touch myfile{1..7}
$ ls
myfile1 myfile2 myfile3 myfile4 myfile5 myfile6 myfile7
{1..7} is called "brace expansion". Bash will expand the expression myfile{1..7} to the list of seven files that we want.
Brace expansion does have a limitation. It does not support shell variables. So, touch myfile{1..$filenumber} would not work. We have to enter the correct number in the braces directly.
Maybe it's a typo: $fiename instead of $filename
also, you might want some kind of loop like so:
x=1
while [ $x -le $filenumber ]; do
touch $filename$x
let x=x+1
done
#!/bin/bash
echo "what file name do you want?"; read filename
echo "how many files do you want"; read filenumber
x=$filenumber
while [ $x -gt 0 ]; do
touch $filename$x
x=$(( $x - 1))
done
Related
I'm a beginner getting started with bash scripting.
I have 10 directories in current working directory dir1-dir10 + script.sh + a file called "tocopyfile".
Dir1-10 are empty.
tocopyfile is a test text file to be used for the purpose of my training
script.sh contains the following code:
dir=`pwd`
i="0"
for directory in `ls $dir`;do
while [ $i -le 10 ]
do
cp tocopyfile $directory/file$i &
i=$[$i+1]
done
done
The script should copy 10 copies of the file "tocopyfile" to every dir (dir1-10) in the naming convention file#. The problem is that ones the script is exists after the first directory without executing the while loop to the remaining remaining dirs.
Can someone explain what I'm doing wrong please?
Help is greatly appreciated.
The immediate issue is that you need to reset the value of i for each iteration of the outer loop.
for directory in `ls $dir`; do # No! but more on that in a moment
i=0
while [ $i -le 10 ]
There are a few other issues with your code.
dir=$(pwd) is almost always pointless; bash already provides a variable PWD containing the name of the current working directory. You don't actually need this, though; you can simply use ./*/ to expand to a list of directories in the current working directory.
Never use the output of ls in a script.
$[...] is obsolete syntax; use $((...)) instead.
Cleaning your code up a bit, we get
for directory in ./*/; do
i=0
while [ "$i" -le 10 ]; do
cp tocopyfile "$directory/file$i" &
i=$((i+1))
done
done
You need to initialize $i inside the for loop, such that $i == 0 upon each iteration of your while:
dir=`pwd`
for directory in `ls $dir`;do
i="0" # <===== notice the change here
while [ $i -le 10 ]
do
cp tocopyfile $directory/file$i &
i=$[$i+1]
done
done
Other things you might want to change:
Double-quote all your variables (in the event they have spaces in them).
Use $() instead of the long-deprecated back-tick syntax.
Use $(()) instead of the deprecated $[] syntax.
Tidy up your indentation.
I am trying to write a bash script that will do the following:
Take a directory or file as input (will always begin with /mnt/user/)
Search other mount points for same file or directory (will always begin with /mnt/diskx)
Return value
So, for example, the input will be "/mnt/user/my_files/file.txt". It will search if ""/mnt/disk1/my_files/file.txt" exists and will incrementally look for each disk (disk2, disk3, etc) until it finds it or disk20.
This is what I have so far:
#/user/bin/bash
var=$1
i=0
while [ -e $check_var = echo $var | sed 's:/mnt/user:/mnt/disk$i+1:']
do
final=$check_var
done
It's incomplete yes, but I am not that proficient in bash so I'm doing a little at a time. I'm sure my command won't work properly yet either but right now I am getting an "unexpected end of file" and I can't figure out why.
There are many issues here:
If this is the actual code you're getting "unexpected end of file" on, you should save the file in Unix format, not DOS format.
The shebang should be #!/usr/bin/bash or #!/bin/bash depending on your system
You have to assign check_var before running [ .. ] on it.
You have to use $(..) to expand a command
Variables like $i are not expanded in single quotes
sed can't add numbers
i is never incremented
the loop logic is inverted, it should loop until it matches and not while it matches.
You'd want to assign final after -- not in -- the loop.
Consider doing it in even smaller pieces, it's easier to debug e.g. the single statement sed 's:/mnt/user:/mnt/disk$i+1:' than your entire while loop.
Here's a more canonical way of doing it:
#!/bin/bash
var="${1#/mnt/user/}"
for file in /mnt/disk{1..20}/"$var"
do
[[ -e "$file" ]] && final="$file" && break
done
if [[ $final ]]
then
echo "It exists at $final"
else
echo "It doesn't exist anywhere"
fi
I am quite new to bash scripting, but haven't found an answer to the following problem yet. I hope somebody can either tell me or give me tips on how to do it.
Background: I have a program (say "program") that accepts an ini-file (say "input.ini") as input taking a while to execute. A variable in the ini-file for the programm might be "number" for instance, which might be set to number=1.
Problem: I have to call ./program input.ini quite often, but with different values for "number", say 1,2,3,4,5.
I thought, I could write a bash script executing the program in a for-loop setting "number" accordingly. The loop is not a problem, but setting "number" in the ini-file. (I tried e.g. number=$VALUE in the ini-file with VALUE being set in the script, but this does not work.)
Question: How can I set a variable in the ini-file using a bash-script? (This does not have to be permanent, but only for that run of the program.)
Additional question: Setting a variable in the ini-file is one thing. In addition, it would be great to do the following as well (I thought that might work similarly...): The program produces some output files. The names of these files can also be set in the ini-file, say via "output_name=filename.out". It would be great now if there was something like "output_name=filename_$VALUE.out" to set the output names accordingly.
I hope it is clear what I try to do and I would be really grateful if somebody had a solution or hints on how to do it.
Thanks,
Cari
If you have a file that contains number=something, you can replace "something" with "5" using sed "/^number=/s/=.*/=5/.
This is something you can do once off with process substituion:
./program <(sed "/^number=/s/=.*/=5/" baseinput.ini)
Or you can create a new ini file based on the old one, as in
sed "/^number=/s/=.*/=5/" baseinput.ini > input.ini
./program input.ini
You could also define the entire ini file the script, and substitute in a here document:
N=5
./program - << EOF
[Section]
number=$N
foo=bar
EOF
full parsed and set ini file (section,key,value) and save on root.
sudo_setini ()
{
fkey=false
fsec=false
tsec=false
res=""
if [ -f "$1" ]; then
while IFS= read -r LINE
do
TLINE=`echo $LINE`
if [[ $TLINE == \[*] ]]; then
TLINE=`echo ${TLINE:1:${#TLINE}-2}`
if [[ _${TLINE} == _$2 ]]; then
tsec=true
fsec=true
else
if $tsec && ! $fkey ; then
res+=$'\n'$3=$4
fi
tsec=false
fi
res+=$'\n'${LINE}
else
TLINE=`echo ${TLINE%%=*}`
if $tsec && [[ _${TLINE} == _$3 ]]; then
fkey=true
res+=$'\n'${LINE%%=*}=$4
else
res+=$'\n'${LINE}
fi
fi
done < $1
fi
if $tsec && ! $fkey ; then
res+=$'\n'$3=$4
fi
if ! $fsec ; then
res+=$'\n'[$2]
res+=$'\n'$3=$4
fi
echo "$res" | sudo tee "$1" > /dev/null
}
sudo_setini 'test.ini' 'General' 'Type' 'Digital_'
Not quite sure whether this helps or not:
This calls the program script five times:
for n in 1 2 3 4 5
do
./program $n input.ini
done
Then in program, refer to the first parameter $n as $1.
The second parameter input.ini is $2.
If you have git available and you're not worried about indentation, a hack could be to use git config.
Example:
$ git config -f settings.ini server.ip 123.123.123.123
$ cat settings.ini
[server]
ip = 123.123.123.123
$ git config -f settings.ini server.ip 123.123.123.124
$ cat settings.ini
[server]
ip = 123.123.123.124
#!/bin/bash
echo input directory 1
read dir1
dir1a="$dir1"
echo input directory 2
read dir2
dir2a="$dir2"
pathIs="/$dir1/$dir2"
cd $pathIs
echo $pathIs
echo input file name
read file
find . -iname "$file" -print
The above script works fine so long i know how many directories and subdirectories i am going to search (2 in the above example).
My questions:
1. I would like to modify it to work to any number of subdirectories (example 3 or 4)?
2. Even I use for loop to enter multiple directories, i get the problem how to tell the
pathIs variable, how many directories i have entered?
#!/bin/bash
echo directory name
read num
for i in $num
do
echo input directory 1
read dir
done
pathIs="/$dir1/$dir2"
cd $pathIs
echo input file name
read file
find . -iname "$file" -print
There are two options.
Ask the user in advance how many entries will be provided, then read that many times
Let the user terminate their entries, perhaps by giving an empty entry
To do the second option, you could use a general pattern in most programming languages - a "read ahead, read while" loop.
read x
while [ $x ]; do
echo "Do something with $x"
read x
done
echo "DONE"
So we read x once. Then we go into a while loop, which checks the variable, does something with it, then reads into it again, and keeps going until a terminating condition is encountered.
In the example above, if [ $x ] evaluates to false (an empty string), then the loop exits. Instead of echo "Do something with $x", you'll want to append the string to the pathIs variable.
One other thing -- most useful shell scripts don't read inputs of that kind from stdin. Eventually you'll want to process parameters given at the command line instead.
You can also read input arguments from the command line like this:
for i in {1..$#}
do
variable[i]=$1
shift
done
The loop goes through all command line arguments, reading them into the "variable" array
I'm not entirely new to programming, but I'm not exactly experienced. I want to write small shell script for practice.
Here's what I have so far:
#!/bin/sh
name=$0
links=$3
owner=$4
if [ $# -ne 1 ]
then
echo "Usage: $0 <directory>"
exit 1
fi
if [ ! -e $1 ]
then
echo "$1 not found"
exit 1
elif [ -d $1 ]
then
echo "Name\t\tLinks\t\tOwner\t\tDate"
echo "$name\t$links\t$owner\t$date"
exit 0
fi
Basically what I'm trying to do is have the script go through all of the files in a specified directory and then display the name of each file with the amount of links it has, its owner, and the date it was created. What would be the syntax for displaying the date of creation or at least the date of last modification of the file?
Another thing is, what is the syntax for creating a for loop? From what I understand I would have to write something like for $1 in $1 ($1 being all of the files in the directory the user typed in correct?) and then go through checking each file and displaying the information for each one. How would I start and end the for loop (what is the syntax for this?).
As you can see I'm not very familiar bourne shell programming. If you have any helpful websites or have a better way of approaching this please show me!
Syntax for a for loop:
for var in list
do
echo $var
done
for example:
for var in *
do
echo $var
done
What you might want to consider however is something like this:
ls -l | while read perms links owner group size date1 date2 time filename
do
echo $filename
done
which splits the output of ls -l into fields on-the-fly so you don't need to do any splitting yourself.
The field-splitting is controlled by the shell-variable IFS, which by default contains a space, tab and newline. If you change this in a shell script, remember to change it back. Thus by changing the value of IFS you can, for example, parse CSV files by setting this to a comma. this example reads three fields from a CSV and spits out the 2nd and 3rd only (it's effectively the shell equivalent of cut -d, -f2,3 inputfile.csv)
oldifs=$IFS
IFS=","
while read field1 field2 field3
do
echo $field2 $field3
done < inputfile.csv
IFS=oldifs
(note: you don't need to revert IFS, but I generally do to make sure that further text processing in a script isn't affected after I'm done with it).
Plenty of documentation out the on both for and while loops; just google for it :-)
$1 is the first positional parameter, so $3 is the third and $4 is the fourth. They have nothing to do with the directory (or its files) the script was started from. If your script was started using this, for example:
./script.sh apple banana cherry date elderberry
then the variable $1 would equal "apple" and so on. The special parameter $# is the count of positional parameters, which in this case would be five.
The name of the script is contained in $0 and $* and $# are arrays that contain all the positional parameters which behave differently depending on whether they appear in quotes.
You can refer to the positional parameters using a substring-style index:
${#:2:1}
would give "banana" using the example above. And:
${#: -1}
or
${#:$#}
would give the last ("elderberry"). Note that the space before the minus sign is required in this context.
You might want to look at Advanced Bash-Scripting Guide. It has a section that explains loops.
I suggest to use find with the option -printf "%P\t%n\t%u\t%t"
for x in "$#"; do
echo "$x"
done
The "$#" protects any whitespace in supplied file names. Obviously, do your real work in place of "echo $x", which isn't doing much. But $# is all the junk supplied on the command line to your script.
But also, your script bails out if $# is not equal to 1, but you're apparently fully expecting up to 4 arguments (hence the $4 you reference in the early part of your script).
assuming you have GNU find on your system
find /path -type f -printf "filename: %f | hardlinks: %n| owner: %u | time: %TH %Tb %TY\n"