GAAAAAAAAAAAAAAH I don't know why I'm struggling so much with this.
All I want to do is make:
file="$a"[command-here]"$b"
to make $file=
a [newline]
b
And yet no matter WHAT echo or printf I try to use, I cannot get it to work!! I just want a newline between a and b. Can someone bring me out of my misery?
file="$a""echo -e '\n'""$b" does not work, nor can any combination I can think of.
You can use:
a='foo'
b='bar'
file="$a"$'\n'"$b"
echo "$file"
foo
bar
Or use print -v:
unset file
printf -v file "$a\n$b"
echo "$file"
foo
bar
sh and similar shells preserve newlines in quoted strings. You could do this:
file="$a
$b"
for example:
$ a=foo
$ b=bar
$ file="$a
> $b"
$ echo "$file"
foo
bar
Related
I'm trying to write a shell script that takes in a file(ex. file_1_2.txt) and replaces any "_" with "."(ex. file.1.2.txt). This is what I have but its giving me a blank output when I run it.
read $var
x= `echo $var | sed 's/\./_/g'`
echo $x
I'm trying to store the changed filename in the variable "x" and then output x to the console.
I am calling this script by writing
./script2.sh < file_1_2.txt
There is two problems. First, your code has some bugs:
read var
x=`echo $var | sed 's/_/\./g'`
echo $x
will work. You had an extra $ in read var, a space too much (as mentioned before) and you mixed up the replacement pattern in sed (it was doing the reverse of what you wanted).
Also if you want to replace the _ by . in the filename you should do
echo "file_1_2.txt" | ./script2.sh
If you use < this will read the content of `file_1_2.txt" into your script.
Another solution, with bash only:
$ x=file_1_2.txt; echo "${x//_/.}"
file.1.2.txt
(See “Parameter expansion” section in bash manual page for details)
And you can also do this with rename:
$ touch file_1_2.txt
$ ls file*
file_1_2.txt
$ rename 'y/_/\./' file_1_2.txt
$ ls file*
file.1.2.txt
Threre is not need for sed as bash supports variable replacement:
$ cat ./script2
#!/bin/bash
ofile=$1
nfile=${ofile//_/./}
echo mv "$ofile" "$nfile"
$ ./script2 file_1_2.txt
mv "file_1_2.txt" "file.1.2.txt"
Then just remove echo if you are satisfied with the result.
I have a bash script like
source ./testscript
while read line
do
echo "$line"
done < "test.file"
where the testscript is like
VAR="hello"
the test.file is like
"$VAR" world!!!
I expect the output of my bash script to be
hello world!!!
but what i get is
"$VAR" world!!!
Is there any solution?
you can try
echo `eval "echo $line"`
instead of
echo "$line"
You don't want to use eval. eval would allow arbitrary code to be embedded into your (text?) file which is certainly not what you want, especially if untrusted users have write access to said file.
If you don't need to expand arbitrarily named variables you can do something like the following, but there might be a better approach to the problem you are actually trying to solve.
$ cat t.sh
#!/bin/bash
VAR1=hello
VAR2='!!!'
FOO=test
expand_vars()
{
local s=$*
local var
for var in VAR{1..9} FOO; do
s=${s//%${var}%/${!var}}
done
echo "${s}"
}
while read line; do
line=$(expand_vars "${line}")
echo "${line}"
done <<__DATA__
%VAR1% world%VAR2%
This is a %FOO%
__DATA__
.
$ ./t.sh
hello world!!!
This is a test
In my script I'm trying to take a string, then output the extension of a string if it has one. So essentially I take the basename of a file, then output anything that comes after a period.
What's the syntax to do something like this?
For example
dotcutter.sh
file=testfile.jpg
the script should output .jpg
EDIT:
I've solved my problem now with:
file=$(basename "$1" )
stub=${file%.*}
echo ${file#"$stub"}
Which reduces my argument to a basename, thank you all.
$ file=testfile.jpg
$ echo ${file##*.}
jpg
$ file=testfile.ext1.ext2
$ echo ${file##*.}
ext2
$ file=noextension
$ echo ${file##*.}
noextension
Notice that it doesn't work if the file has no extension. If that's important then try this two-step solution:
$ file=testfile.ext1.ext2
$ stub=${file%.*}
$ echo ${file#"$stub"}
.ext2
Or this regex-based one, which will only call echo if there's actually an extension. (&& is shorthand for "if then".)
$ regex='(\.[^.]*)$'
$ file=testfile.ext1.ext2
$ [[ $file =~ $regex ]] && echo ${BASH_REMATCH[1]}
.ext2
$ file=noextension
$ [[ $file =~ $regex ]] && echo ${BASH_REMATCH[1]}
See also:
basename(1)
dirname(1)
Bash FAQ: How can I use parameter expansion? How can I get substrings? How can I get a file without its extension, or get just a file's extension?]
Try the following code :
file=testfile.jpg
echo .${file#*.}
That use parameter expansion
I isolated a problem in my script to this small example. That's what I get:
$ cmd="test \"foo bar baz\""
$ for i in $cmd; do echo $i; done
test
"foo
bar
baz"
And that's what I expected:
$ cmd="test \"foo bar baz\""
$ for i in $cmd; do echo $i; done
test
"foo bar baz"
How can I change my code to get the expected result?
UPDATE Maybe my first example was not good enough. I looked at the answer of Rob Davis, but I couldn't apply the solution to my script. I tried to simplify my script to describe my problem better. This is the script:
#!/bin/bash
function foo {
echo $1
echo $2
}
bar="b c"
baz="a \"$bar\""
foo $baz
This it the expected output compared to the output of the script:
expected script
a a
"b c" "b
First, you're asking the double-quotes around foo bar baz to do two things simultaneously, and they can't. You want them to group the three words together, and you want them to appear as literals. So you'll need to introduce another pair.
Second, parsing happens when you set cmd, and cmd is set to a single string. You want to work with it as individual elements, so one solution is to use an array variable. sh has an array called #, but since you're using bash you can just set your cmd variable to be an array.
Also, to preserve spacing within an element, it's a good idea to put double quotes around $i. You'd see why if you put more than one space between foo and bar.
$ cmd=(test "\"foo bar baz\"")
$ for i in "${cmd[#]}"; do echo "$i"; done
See this question for more details on the special "$#" or "${cmd[#]}" parsing feature of sh and bash, respectively.
Update
Applying this idea to the update in your question, try setting baz and calling foo like this:
$ baz=(a "\"$bar\"")
$ foo "${baz[#]}"
Why quote it in the first place?
for i in test "foo bar baz"; do echo $i; done
Let's say I have a path which looks like the following:
/A/B/C/DIRECTORY/D/E/F
Now, what I want to achieve using parameter substitution, is cutting of the part of the path after DIRECTORY, no matter where in the path the DIRECTORY is positioned (A, B, C, etc. just stand for random directory names).
After substitution:
/A/B/C/DIRECTORY
This is what I've tried so far:
#!/bin/bash
if [[ $# < 1 ]]
then
echo "Usage: $0 DIRECTORY"
fi
CURRENT_DIRECTORY=$(pwd)
DIRECTORY=$1
cd ${CURRENT_DIRECTORY%!(DIRECTORY)/*}
Obviously, this doesn't work. I could implement this using awk or sed but I'm interested if it is possible using only parameter substitution.
May be this can help -
[jaypal:~/Temp] D="A/B/C/DIRECTORY/D/E/F"
[jaypal:~/Temp] echo $D
A/B/C/DIRECTORY/D/E/F
[jaypal:~/Temp] c=${D%/*/*/*}
[jaypal:~/Temp] echo $c
A/B/C/DIRECTORY
[ghoti#pc ~]$ D="/A/B/C/foo/D/E"
[ghoti#pc ~]$ echo "${D/foo*/}"
/A/B/C/
of course, this isn't perfect:
[ghoti#pc ~]$ D="/A/B/C/foobar/D/foo/E"
[ghoti#pc ~]$ echo "${D/foo*/}"
/A/B/C/
Read the bash man page and search for "Pattern substitution".
Try next solution:
cd ${CURRENT_DIRECTORY/${DIRECTORY}*/""}
EDIT:
More accurate answer would be appending your directory after removing last part:
cd ${CURRENT_DIRECTORY/${DIRECTORY}*/""}$DIRECTORY
Thanks to Birei, I came to this (not perfect but good enough solution:
#/bin/bash
if [[ $# < 1 ]]
then
echo "Usage: $0 BASENAME"
fi
echo ${PWD/%$1*/$1}
Example:
$ PWD=/home/ltorvalds/workspace
$ ./up.sh ltorvalds
/home/ltorvalds