I'm using this script to shortcut a 7z command with prefered options:
#!/bin/bash
7z a -t7z -m0=lzma2 -mx=9 -ms=on -mmt=on "$1".7z $2
If I give it a folder as the second argument it work just fine, but if I send something like *file*.png, it creates the 7z files but there is nothing on it... What I'm doing wrong?
When pressing [Enter] key after writing the script name and its parameters in the terminal, the current bash is expanding the *file*.png: it is replaced by the list of the files found on the disk, and then the script is run.
So, $2 is the first file from the list of the files found by the shell after *file.*.png was expanded: see PARAMETERS and Special Parameters and pathname expansion in the man bash.
As wrote in the comment, a quick win, using the same script, unchanged, is to prevent the pathname expansion using 2 ' chars in the command line:
$ my-7z.sh png-files '*file*.png'
The definitive solution, is to handle the positional parameters:
#!/bin/bash
7z_filename="${1}".7z
shift
7z a -t7z -m0=lzma2 -mx=9 -ms=on -mmt=on "${7z_filename}" "${#}"
Maybe it is worth to check the positional parameters, to add some error management.
Related
I am a beginner of bash. I encounter a problem like this:
$ "make -p"
when I type the above in bash command line, there is nothing to happen, no error, no result msg.
I have searched double quotes syntax of bash in many websites. All of these materials give similar interpretation as below:
https://www.gnu.org/software/bash/manual/html_node/Double-Quotes.html
and give examples like:
echo "argument"
I do not find something like "echo argument". Moreover, I find a strange difference between bash command line and bash scripts.
If I type a non-existing command in command line:
$ "holy shit"
$ "look that"
there is nothing to happen. But if I type it in bash scripts:
#!/bin/bash
"holy shit"
"look that"
and execute this script, an error msg will be throw out:
$ ./myshell
./myshell: line 2: holy shit: command not found
./myshell: line 3: look that: command not found
Would someone can help give a detailed interpretation about the effect of double quotes when they enclosed the whole command?
Why there is no output in command-line?
Why it is different between command line and scripts?
If you enter a command foo, the shell searches the directories listed in your PATH variable until it finds a command of this name. If there is none, you get the error message command not found.
If you enter a command, which contains at least one slash - for example ./foo or foo/bar -, the shell does not search the PATH, but assumes that you have already entered the correct path to your command. If it does not exist, you get the error message No such file or directory.
In your case,
"cd home"
searches for a file with name cd home somewhere along your PATH, but there is no file of this name, and you get command not found. If you enter
"cd /home"
the shell bypasses PATH-search and assumes, that there exists a directory named cd (i.e. the 3 letters c,d,space) in your current directory, and below it a file named home, with x-bit set. There is no such file (and no such directory) on your system, and you get the error message No such file or directory.
If you are in the mood of experimenting around, you could try the following:
mydir="cd "
mkdir "$mydir"
echo "echo Hello Stranger" >"$mydir/home"
chmod +x "$mydir/home"
"cd /home"
This should print Hello Stranger. Pay attention that in the assignment to mydir, there must be a single space between the cd and the closing quote.
The double quotes mean it is a string. You can do something like:
echo "Hello everybody"
either at the command line or the shell. Sometimes when people put stuff in quotes. you are supposed to replace what is in quotes with your own variable (removing the quotes), and sometimes people put quotes around the whole command you are supposed to type to show the what exactly you should type. For your example of "make -p" just type it without the quotes and it should work in both the command line and as a script.
I'm on WSL (windows subsystem for Linux)
I'm trying to create an alias to run vswhere.exe, which will tell me where devenv.exe is located, and then run that from my ZSH shell.
path1="$(vswhere.exe -property productPath -format value)"
echo $path1
outputs: C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\Common7\IDE\devenv.exe
path2=$(wslpath -a "$path1")
echo $path2
outputs: /mnt/c/Program Files (x86)/Microsoft Visual Studio/2017/Enterprise/Common7/IDE/devenv.exe
$path2 contains exactly what i want to execute, except it's not quoted. If I take that string on the console, copy paste it, and surround it with quotes manually, VS executes properly.
Like this: eval '/mnt/c/Program\ Files\ (x86)/Microsoft\ Visual\ Studio/2017/Enterprise/Common7/IDE/devenv.exe'
running: eval $path2 fails since the $path2 has spaces in it.
I've tried this: path3=$(printf %q $path2 | sed -e 's/^M$//')
echo $path3
'mnt/c/Program\ Files\ (x86)/Microsoft\ Visual\ Studio/2017/Enterprise/Common7/IDE/devenv.exe$'
This contains a $ at the end of the string. Trying to do eval $path3 fails with:
zsh: no such file or directory: /mnt/c/Program Files (x86)/Microsoft Visual Studio/2017/Enterprise/Common7/IDE/devenv.exe^M
Note the ^M at the end. I've run out of ideas on sed commands and such to make this work and I'm sure there's just some fancy regex or zsh/bash command to make this work properly.
You need to quote the expansion of $path2. You don't need eval. You do need to strip the trailing carriage return if it is present. The reason is that if wslpath produces output with a DOS line ending (\r\n), the command substitution strips the trailing newline (\n) character, but not the carriage return (\r) that precedes it, leaving it as an ordinary character.
# Get the path
path2=$(wslpath -a "$path1")
# Strip the trailing carriage return, if present
path2="${path2%$'\r'}"
# Execute the program, quoting the expansion
# to produce a single shell word for the command name.
"$path2"
Read more carefully the bash reference manual. Be aware of quotations and how to use them wisely.
You should consider:
eval '"/mnt/c/Program\ Files\ (x86)/Microsoft\ Visual\ Studio/2017/Enterprise/Common7/IDE/devenv.exe"'
Better yet, get rid of that spaces in system directory madness (MicroSoft is crazy to use them) and have some symlinks like /mnt/c/Program_Files_x86 -> /mnt/c/Program\ Files\ (x86) etc. How to set that up is left as an exercise to the reader.
Actually, I would recommend having your $HOME/bin/ in your $PATH and put a symlink to /mnt/c/Program\ Files\ (x86)/Microsoft\ Visual\ Studio/2017/Enterprise/Common7/IDE/devenv.exe (which I guess is some MicroSoft IDE) in your $HOME/bin/
Your life will be simpler if you avoid like plague spaces (and some other weird characters such as *, (, ), [, ], ;, ? etc...) in file names. Because spaces in command lines are related to globbing and shell expansion.
BTW, I can't understand why you need to use devenv.exe from a Linux script. IMNSHO you should avoid using MicroSoft IDEs from a Linux script. I really believe you should avoid doing this. See this answer, it is relevant to your issue. Don't use Linux like you use Windows. So use Windows the Windows way, and use Linux -and that includes WSL- the Linux way (read about the Unix philosophy).
Regarding usage of sed (or the printf command...) take some time to read the sed(1) man page (and also printf(1)). That documentation is clear enough (but dense).
Originally I would like to sync directory (with all files and subdirectories) given in parameter in bash script.
I found this post: How can I recursively copy a directory into another and replace only the files that have not changed? which explains how to use rsync in similar case.
My bash script is quite simple and listed below:
#!/bin/bash
echo -e "Type the project to be deployed: \c "
read project
echo -e "* Deploying: $project *"
echo -e "Sync: /var/repo/released/$project"
echo -e " /var/www/released/$project"
rsync -pr /var/repo/released/$project /var/www/released/$project
As a result it copies everything within /released (there are many directories in there, let's say -projects-).
I would like to copy (sync) only project given in parameter.
Could you please advice how to do this?
When you call the script without an argument (which most likely is what you're doing since you interactively read the project name into the variable $project), the positional parameter $1 remains empty. Therefore the script will rsync the entire content of /var/repo/released/.
You need to replace $1 with $project in your script. Also, I'd recommend to put double quotes around the paths to avoid problems due to spaces in a directory name.
rsync -pr "/var/repo/released/$project" "/var/www/released/$project"
We have simple Windows batch files that when an error occurs, an "ONCALL.bat" file is run to display support information that is maintained in a separate oncall.txt text file. This is our SOP.
ONCALL.BAT:
set scriptpath=%~dp0
TYPE "%scriptpath%oncall.txt"
I have zero experience with Unix and Shell scripts and I need to quickly provide a shell script equivalent to run in a Unix environment.
Could someone please provide me the .sh equivalent of this code?
Assuming that the help file and the script are in the same directory:
#!/bin/sh
SCRIPTPATH=`dirname "$0"`
cat "$SCRIPTPATH"/oncall.txt
$0 is the file path of the current script; the dirname command extracts the directory part of it. This way you can avoid using a hard-coded path for the help file within the script.
cat oncall.sh
#!/bin/bash
scriptpath=/path/to/scripts
cat ${scriptpath}/oncall.txt
After you create your file, it can't hurt to run
dos2unix oncall.sh
Just to be sure there are no windows Ctrl-M chars that will totally mystify you with the way they can screw up Unix script processing.
THEN
chmod 755 oncall.sh
To make the script executable.
confirm with
ls -l oncall.sh
You should see listing like
-rwxr-xr-x 1 userName grpname 5263 Nov 21 14:44 oncall.sh
Finally, call the script with a full or relative path, i.e.
./oncall.sh
OR
$PWD/oncall.sh
The first line is called the "shebang" line, and when your script is called, the OS reads the first line of the file, to find out what program to run to interpret the rest of the script file.
You may want/need to use as the first line "shebang" one of the following, but bash is a good guess
#!/bin/ksh
#!/bin/sh
#!/bin/ash
#!/bin/dash
#!/bin/zsh
OR you may worst case, your shell lives in a non-standard directory, then you'll have to spell that out, i.e.
#!/usr/bin/ksh
All shell support debugging arguments for trace and variable expansion like
#!/bin/ksh -vx
Or you can wrap just certain lines to turn debugginng on and off like
set -vx
cat ${scriptpath}/oncall.txt
set +vx
Given that
The ~dp special syntax between the % and the 0 basically says to expand the variable %0 to show the drive letter and path, which gives you the current directory containing the batch file!
I think /path/to/scripts is a reasonable substitute, scriptpath=$PWD would be a direct replacement, as there are no drive letters in Unix. The problem there, is that you either rely on unix PATH var to find your script or you cd /path/to/scripts and then run ./oncall.sh using the relative path./ to find the file without naving added a value to PATH.
IHTH.
Is there a way to do command substitution in BASH shell without breaking output into multiple arguments?
I copy the path of some directory (from the location bar in a GUI file browser) to clipboard and then issue the following command, where the command xsel returns the clipboard content, which is the path of the directory in this case:
cd `xsel`
But some path contain spaces or may even contain some special characters used by BASH.
How can I pass the output of a command as a single argument and without BASH messing with special characters?
cd "$(xsel)"
seems to handle all special characters (including $ and spaces).
My test string was boo*;cd.*($\: $_
$ mkdir "$(xsel)"
$ ls
boo*;cd.*($\: $_
$ file boo\*\;cd.\*\(\$\\\:\ \$_/
boo*;cd.*($\: $_/: directory
$ cd "$(xsel)"
$ pwd
/tmp/boo*;cd.*($\: $_
Have you tried:
cd "`xsel`"
That should do the job, unless you have dollars($) or back-slashes (\) in your path.
If you aren't doing this programmatically, most terminals in Linux let you paste from the clipboard with a middle-click on your mouse. Of course, you'll still need to put quotes before and after your paste, like #dave suggests.