I am new to programming and I was trying to add the SSH key to my GitHub account (using mac interface). And when I was trying to copy the generated key using the command:
~ pbcopy < ~/testkey.pub
It was showing me this error
zsh: permission denied: /Users/myName
Someone please help me.
In a basic shell command, the first "word" is the name of the program to run (i.e. the name of the executable that'll actually perform the command). For example, if you run the command ls -l ~/testkey.pub, it actually runs a program named "ls" (probably in the /bin directory, so its full path is /bin/ls), and passes it the arguments "-l" and "/Users/myName/testkey.pub".
"Wait", I hear you say, "where'd that '/Users/myName' bit come from?" Well, ~ is shell shorthand for the path to your home directory, and when the shell sees it as part of a command (well, depending on the exact context), it'll replace it with the path to your home directory. Thus, ~/testkey.pub gets expanded to /Users/myName/testkey.pub. Try it with echo ~ and you'll see what I mean.
Now, when you try to run the command
~ pbcopy < ~/testkey.pub
the shell expands out the ~s, giving:
/Users/myName pbcopy < /Users/myName/testkey.pub
and since "/Users/myName" is the first word of this command, it tries to execute that as a program. But your home directory isn't an executable program, it's a directory, and you aren't allowed to execute directories, so you get a permissions error.
I'm pretty sure the ~ isn't actually supposed to be part of the command at all. I think you just want to run:
pbcopy < ~/testkey.pub
...which runs the /usr/bin/pbcopy executable, and feeds it input from the file /Users/myName/testkey.pub.
Related
I am attempting to work with an existing library of code but have encountered an issue. In short, I execute a shell script (let's call this one A) whose first act is to call another script (B). Script B is in my current directory (a requirement of the program I'm using). The software's manual makes reference to bash, however comments in A suggest it was developed in ksh. I've been operating in bash so far.
Inside A, the line to execute B is simply:
. B
It uses the "dot space" syntax to call the program. It doesn't do anything unusual like sudo.
When I call A without dot space syntax, i.e.:
./A
it always errors saying it cannot find the file B. I added pwd, ls, whoami, echo $SHELL, and echo $PATH lines to A to debug and confirmed that B is in fact right there, the script is running with the same $SHELL as I am at the command prompt, the script is the same user as I am, and the script has the same search path $PATH as I do. I also verified if I do:
. B
at the command line, it works just fine. But, if I change the syntax inside A to:
./B
instead, then A executes successfully.
Similarly, if I execute A with dot space syntax, then both . B and ./B work.
Summarizing:
./A only works if A contains ./B syntax.
. A works for A with either ./B or . B syntax.
I understand that using dot space (i.e. . A) syntax executes without forking to a subshell, but I don't see how this could result in the behavior I'm observing given that the file is clearly right there. Is there something I'm missing about the nuances of syntax or parent/child process workspaces? Magic?
UPDATE1: Added info indicating that the script may have been developed in ksh, while I'm using bash.
UPDATE2: Added checking to verify $PATH is the same.
UPDATE3: The script says it was written for ksh, but it is running in bash. In response to Kenster's answer, I found that running bash -posix then . B fails at the command line. That indicates that the difference in environments between the command line and the script is that the latter is running bash in a POSIX-compliant mode, whereas the command line is not. Looking a little closer, I see this in the bash man page:
When invoked as sh, bash enters posix mode after the startup files are read.
The shebang for A is indeed #!/bin/sh.
In summary, when I run A without dot space syntax, it's forking to its own subshell, which is in POSIX-compliant mode because the shebang is #!/bin/sh (instead of, e.g., #!/bin/bash. This is the critical difference between the command line and script runtime environments that leads to A being unable to find B.
Let's start with how the command path works and when it's used. When you run a command like:
ls /tmp
The ls here doesn't contain a / character, so the shell searches the directories in your command path (the value of the PATH environment variable) for a file named ls. If it finds one, it executes that file. In the case of ls, it's usually in /bin or /usr/bin, and both of those directories are typically in your path.
When you issue a command with a / in the command word:
/bin/ls /tmp
The shell doesn't search the command path. It looks specifically for the file /bin/ls and executes that.
Running ./A is an example of running a command with a / in its name. The shell doesn't search the command path; it looks specifically for the file named ./A and executes that. "." is shorthand for your current working directory, so ./A refers to a file that ought to be in your current working directory. If the file exists, it's run like any other command. For example:
cd /bin
./ls
would work to run /bin/ls.
Running . A is an example of sourcing a file. The file being sourced must be a text file containing shell commands. It is executed by the current shell, without starting a new process. The file to be sourced is found in the same way that commands are found. If the name of the file contains a /, then the shell reads the specific file that you named. If the name of the file doesn't contain a /, then the shell looks for it in the command path.
. A # Looks for A using the command path, so might source /bin/A for example
. ./A # Specifically sources ./A
So, your script tries to execute . B and fails claiming that B doesn't exist, even though there's a file named B right there in your current directory. As discussed above, the shell would have searched your command path for B because B didn't contain any / characters. When searching for a command, the shell doesn't automatically search the current directory. It only searches the current directory if that directory is part of the command path.
In short, . B is probably failing because you don't have "." (current directory) in your command path, and the script which is trying to source B is assuming that "." is part of your path. In my opinion, this is a bug in the script. Lots of people run without "." in their path, and the script shouldn't depend on that.
Edit:
You say the script uses ksh, while you are using bash. Ksh follows the POSIX standard--actually, KSH was the basis for the POSIX standard--and always searches the command path as I described. Bash has a flag called "POSIX mode" which controls how strictly it follows the POSIX standard. When not in POSIX mode--which is how people generally use it--bash will check the current directory for the file to be sourced if it doesn't find the file in the command path.
If you were to run bash -posix and run . B within that bash instance, you should find that it won't work.
Trying to troubleshoot this issue:
https://unix.stackexchange.com/questions/128894/ssh-exchange-identification-connection-closed-by-remote-host-not-using-hosts-d
and part of one solution is to use:
$(which sshd)
which in my case outputs:
Could not load host key: /etc/ssh_host_rsa_key
Could not load host key: /etc/ssh_host_dsa_key
I keep a cheat sheet for all my bash commands and wanted to add in:
$()
It appears to be doing something to the sshd executable.
Related
What does it mean in shell when we put a command inside dollar sign and parentheses: $(command)
$() is a way to execute another process and collect its output. See http://wiki.bash-hackers.org/syntax/expansion/cmdsubst for more details.
When such expression is passed to bash, its output gets executed. It effectively call the command using its full path, as that is what which returns. The messages printed are from sshd process started by that expression.
Note that which locates executable scanning $PATH, same as when you execute the command. In other words, executing which output it is not going to affect which executable is run, only the full path to executable tracked by operating system.
$(which sshd) will be replaced by the stdout resulting from running which sshd. which sshd will return the fully-qualified path of the executable invoked when invoking sshd:
which returns the pathnames of the files (or links) which would be executed in the current environment, had its arguments been
given as commands in a strictly POSIX-conformant shell. It does this by searching the PATH for executable files matching the
names of the arguments. It does not follow symbolic links.
Examples, as run on a command line, where > represents the input prompt:
COMMAND: which sshd
OUTPUT: /usr/sbin/sshd
COMMAND: echo "The full path of sshd is $(which sshd)"
OUTPUT: The full path of sshd is /usr/sbin/sshd
COMMAND: $(which sshd)
OUTPUT: [[whatever output you get from running /usr/sbin/sshd]]
I have a Windows batch script (to be honest, it's a Groovy script). In this script I determine the root directory of a Cygwin installation. The next step is to find out the current user and home directory. In Cygwin this would be just a
echo $HOME
#=> /home/Christian
What comes pretty close to my problem is the following question: Get results of command from Cygwin in Batch.
I would like to execute something like this:
"C:\cygwin[64]\bin\bash[64].exe" "echo $HOME"
However I receive a
#=> /usr/bin/bash[64]: echo $HOME: No such file or directory
This is because bash is expecting a script file and I want to execute a single command. How can this be done? Is there a possibility without putting the command in a script file?
My goal is to get the Windows path to the current users home directory so that I can iterate over this directory from a Windows script.
The correct option to issue a single command is -c, and before you need to perform a login with --login.
C:\>c:\cygwin64\bin\bash.exe --login -c "cygpath -w $HOME"
#=> C:\cygwin64\home\Christian
I found the answer here.
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.
I've created a bash shell script file that I can run on my local bash (version 4.2.10) but not on a remote computer (version 3.2). Here's what I'm doing
A script file (some_script.sh) exists in a local folder
I've done $ chmod 755 some_script.sh to make it an executable
Now, I try $ ./some_script.sh
On my computer, this runs fine. On the remote computer, this returns a Command not found error:
./some_script.sh: Command not found.
Also, in the remote version, executable files have stars(*) following their names. Don't know if this makes any difference but I still get the same error when I include the star.
Is this because of the bash shell version? Any ideas to make it work?
Thanks!
The command not found message can be a bit misleading. The "command" in question can be either the script you're trying to execute or the shell specified on the shebang line.
For example, on my system:
% cat foo.sh
#!/no/such/dir/sh
echo hello
% ./foo.sh
./foo.sh: Command not found.
./foo.sh clearly exists; it's the interpreter /no/such/dir/sh that doesn't exist. (I find that the error message varies depending on the shell from which you invoke foo.sh.)
So the problem is almost certainly that you've specified an incorrect interpreter name on line one of some_script.sh. Perhaps bash is installed in a different location (it's usually /bin/bash, but not always.)
As for the * characters in the names of executable files, those aren't actually part of the file names. The -F option to the ls command causes it to show a special character after certain kinds of files: * for executables, / for directories, # for symlinks, and so forth. Probably on the remote system you have ls aliased to ls -F or something similar. If you type /bin/ls, bypassing the alias, you should see the file names without the append * characters; if you type /bin/ls -F, you should see the *s again.
Adding a * character in a command name doesn't do what you think it's doing, but it probably won't make any difference. For example, if you type
./some_script.sh*
the * is a wild card, and the command name expands to a list of all files in the current directory whose names match the pattern (this is completely different from the meaning of * as an executable file in ls -F output). Chances are there's only one such file, so
./some_script.sh* is probably equivalent to ./some_script.sh. But don't type the *; it's unnecessary and can cause unexpected results.