Mistake this is a duplicate [duplicate] - bash

This question already has answers here:
How to obtain the first letter in a Bash variable?
(7 answers)
Closed 3 years ago.
I am trying to my a custom terminal command. I just learned I am supposed to do it using the Unix script? I don't really know much of what that is and am still trying to figure it out. What I do know is that $1 is an arg is it possible to make it a variable and then get the first letter like you could in python?
EX:
str = 'happy'
str[0] = 'h'

You're asking a few different things here.
I am trying to my a custom terminal command.
That could mean a few different things, but the most obvious meaning is that you want to add an executable to your path so that when you type it at the terminal, it runs just like any other executable on your system. This requires just a few things:
the executable permission must be set.
the file must specify how it can be executed. For interpreted programs such as bash scripts or python scripts, you can do so by beginning the file with a "shebang line" that specifies the interpreter for the file.
the file must be in one of the locations specified by your $PATH.
I just learned I am supposed to do it using the Unix script?
there's no such thing as a "unix script", but what you seem to be referring to is a "shell script". Though these are commonly associated with unix, they're no more inherently a unix script than any other language. A shell, such as bash, sh, or any other, is just an interpreted language that is designed so that it is convenient to be used interactively by a human as well as being programmatically executed as part of a saved file.
I don't really know much of what that is and am still trying to figure it out.
Let's get into some specifics.
First I edit a file called 'hello-world' to contain:
#!/bin/bash
echo "Hello, world!"
Note that this filename has no "extension". Though heuristics based on file extension are sometimes used (espeically in windows) to determine a file type, unix typically sees a file "extension" as part of the arbitrary file name. The thing that makes this a potentially executable bash script is the specification of that interpreter on the shebang line.
We can run our script right now from bash, just as we could if we wrote a python script.
$ bash hello-world
hello, world!
To make the bash implicit, we mark the file as executable. This enables the linux operating system to consult the beginning "magic bytes" of the file to determine how to run it. Thes beginning bytes might signify an ELF file (a compiled executable, written in eg C, C++, or go). Or, it might be #! which just so happens means , "read the rest of this first line to determine the command to run, and pass the rest of this file into that command to be interpreted.
$ chmod +x hello-world
ls -l will show us the "permissions" on the file (more accurately called the "file mode", hence chmod rather than chperm) . The x stands for executable, so we have enabled the use of the leading bytes to determine method of execution. Remember, the first two bytes of this file, and the rest of that first line, then specify that this file should be "run through bash" so to speak.
$ ls -l hello-world
-rwxr-xr-x 1 danfarrell staff 33 Dec 27 20:02 hello-world
Now we can run the file from the current directory:
$ ./hello-world
hello, world!
At this point, the only difference between this command and any other on the system, is that you have to specify its location. That's because my current directory is not in the system path. In short, the path (accessible in a unix shell via the $PATH variable) specifies an ordered list of locations that should be searched for a specified command whose location is not otherwise specified.
For example, there's a very common program called whoami. I can run it directly from my terminal without specifying a location of the executable:
$ whoami
danfarrell
This is because there's a location in my $PATH in which the shell was able to find that command. Let's take a closer look. First, here's my path:
$ echo $PATH
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/go/bin
And there's also a convenient program called whereis which can help show which path elements supply a named executable:
$ whereis whoami
/usr/bin/whoami
Sure enough, whoami is in one of the elements of the $PATH. (Actually I shared a simplified $PATH. Yours might be somewhat longer).
Finally, then, we can get to the last thing. If I put hello-world in one of the $PATH elements, I will be able to invoke it without a path. There are two ways to do this: we can move the executable to a location specified in the path, or we can add a new location to the path. For simplicity's sake I'll choose the first of these.
$ sudo cp hello-world /usr/local/bin/
Password:
I needed to use sudo to write to /usr/local/bin because it's not accessible as my user directly - that's quite standard.
Finally, I've achieved the goal of being able to run my very important program from any location, without specifying the executable's location.
$ hello-world
hello, world!
$ which hello-world
/usr/local/bin/hello-world
It works! I've created what might be described as a "custom terminal command".
What I do know is that $1 is an arg is it possible to make it a variable and then get the first letter like you could in python?
Well, one option would be to simply write the custom terminal command in python. If python is available,
$ which python
/usr/bin/python
You can specify it in a shebang just like a shell can be:
#!/usr/bin/env python
print("hello, world!"[0])
$ hello-world
h
it works!
Okay, confession time. I actually used #!/usr/bin/env python, not /usr/bin/python. env helps find the correct python to use in the user's environment, rather than hard coding one particular python. If you've been using python during the very long running python 2 to python 3 migration, you can no doubt understand why I"m reticent to hard code a python executable in my program.
It's certainly possible to get the first letter of a string in a bash script. But it's also very possible to write a custom command in a program other than shell. Python is an excellent choice for string manipulation, if you know it. I often use python for shell one-liners that need to interact with json, a format that doesn't lend itself well to standard unix tool stream editing.
Anyway, at the expense of incurring SO community's ire by reanswering an "already answered" question, I'll include a version in shell (Credit goes to David C Rankin)
#!/bin/bash
echo "${1:0:1}"
$ hello-world hiworld
h

Related

How to get the absolute path of a script using its PPID - Bash

I am trying the absolute path of a given PPID from a shell script.
However, the Parent Process may be any type of script (bash/csh/zsh/tsh/Makefile).
The child process is always a bash script and is the only script I have access on to edit.
I have tried:
ps --no-headers -o command $PPID but it only gets the command that invoked the parent process. This isn't what I need because the parent may have caused some cd's inside the script and I won't be able to resolve the relative path in the command to it.
ls -l /proc/$PPID/fd/255 and this was the closest to what I want but this is specific to bash scripts and as I mentioned, I don't have access to know my parent process' script type.
/proc/$PPID/exe returns the binary exe, and I need the script's absolute path that is using this binary.
There isn't such thing as "the" absolute path. There may be several way to access the same file. And some times, none of them (or more than one of them, depends how you look at it) are "the" main name.
I am thinking of hard links here, for example.
Makefile is never the path of a script. It is just a configuration file. That is read by default by the executable. Which is probably /usr/bin/make. To find a path to the Makefile file, you need to read arguments of make. Or to find a Makefile among open files (assuming there is only one, and that it is named Makefile, and that is not certain). And the strategy to guess what is the Makefile of a make process is specific to make. You need another strategy to guess what is the script file executed by a bash process, Another to find what is the python file executed by a python command (which could be a shebang python "executable", a python ../somerealtive/path/somefile.py a python /some/absolute/paht a python -m somemodele, a python -c "import somemod ; somemod.run()" etc.)
Now, since you've mentioned /proc/$PPID/exe, you can get the file "pointed" by this, using
path=$(readlink -f /proc/$PPID/exe)
Again, that is linux solution, more than a bash one. You have no guarantee from bash that the system on which you run this command have a /proc filesystem
I must add that I suspect a XY problem here. What are you trying to do exactly?

What is the advantage of doing `chmod` on a `.sh` file?

I see this in a lot of installation instructions for certain libraries, where you're given some installation.sh file, and the instructions say to
chmod + x installation.sh
./installation.sh
I am wondering what is the point of this, when you can just do
sh installation.sh
I'm not well versed with shell scripting.
Running it with sh installtion.sh guarantees you that the script is processed with sh (i.e. you are responsible to know which language the script is written in), and installation.sh must be in your working directory.
Running it with installation.sh (which is only possible if the script is executable) guarantees that the script is processed with that language processor (sh, zsh, python,....) which is defined in the #! line of the script. In addition, installation.sh must be in some directory of your PATH.
It's up to you what you prefer; but if you do a chmod +x, the user of the script can choose whether he is using the first or second variant. If you don't chmod it, the user of the script can only do the first variant.
A (perhaps small) side effect of setting the x-bit is that some tools (for example ls) can be configured to colour executable files differently from non-executable ones, which may be helpful if you do a ls of a bunch of files.

Launch interactive Bash shell in Ruby script, with initial command

I'm working on an interactive Ruby script, which build and packages resources. In the middle of the process, I'd like to drop into an interactive shell, but pre-cd'd into a specific working directory, as well as with an explanatory message (CTRL-D to continue). The interactive bash + given initial command is what's problematic.
Per the answer for doing something like this in Bash, given at https://stackoverflow.com/a/36152028, I've tried
system '/bin/bash', '--init-file', '<(echo "cd ~/src/devops; pwd")'
However, bash runs interactively but completely ignores the '<(echo "cd ~/src/devops; pwd")' section.
Interestingly system '/bin/bash', '--init-file complains if no argument is given, but literally anything runs bash, but with no initial command.
*Note that (--rcfile instead of --init-file) has the same effect.
Change the working directory of the Ruby script first, so that bash inherits the correct working directory.
curr_dir = Dir.pwd
Dir.chdir("#{Dir.home}/src/devops")
system "/bin/bash"
Dir.chdir(curr_dir) # Restore the original working directory if desired
Oh, this is probably far better (you can probably guess how little familiarity I have with Ruby):
system("/bin/bash", :chdir=>"#{Dir.home}/src/devops")

How do I make a Ruby script into a bash command?

I have a Ruby file, and I run it as ruby file.rb "parameters". I prefer to run it as regtask parameters without having to include ruby and the filename every time. I want it to be on the same level as ls. How would I accomplish this?
Edit your file, make sure this is the first line, so your system knows how to execute your file:
#!/usr/bin/env ruby
Next, change the file's permissions to make it executable:
chmod a+x file.rb
And finally, rename it and move it somewhere where it will be executed without having to write its full path:
mkdir -p ~/bin
mv file.rb ~/bin/regtask
(Most systems will automatically add ~/bin to PATH if it exists; if not, you will have to add it to PATH yourself in your startup files.)
This should help.. Please let me know if you run into any issues.
http://commandercoriander.net/blog/2013/02/16/making-a-ruby-script-executable/
Making a Ruby Script Executable
It's common knowledge in the *nix community, but for many new developers turning a Ruby script into an executable command line program is akin to magic. While there are other references on the internet, for the post here, I will briefly explain how to go from running a Ruby script by invoking Ruby directly, to running the script by its name alone.
We will start by assuming we have a simple Ruby script which prints "hello" on the command line. Our script's name will be greeter.rb. The file holds one line of Ruby code:
puts "Hello!"`
To run the script, we must type ruby greeter.rb. Wouldn't it be nice to just type greeter instead and still get the script to run? Yes, it would.
First, we need to tell Bash what to do with our file since we won't be passing the script to Ruby directly. To do that, we add the following to the very top of our script:
#!/usr/bin/env ruby
puts "Hello!"
The first line is a Bash directive and basically tells Bash what program to run our file with by asking for the current configured version of Ruby as specified by the env command. For more on how env works, try typing man env into the command line.
Second, we need to make our script executable, which requires changing the file permissions. If the concept of file permissions is new, read about it here. Bascially, files have three types of permissions. They can be read, written, and executed. Most files typically start out as only having read and write access. Since we want to execute our script, we're going to have to grant it execute permissions.
Doing that is just a simple Bash command. On the command line, navigate to the directory holding the greeter.rb file. Now, to check the permissions, run:
ls -l greeter.rb
The output will look something like this:
-rw-r--r-- 1 username staff 13 Feb 16 21:10 greeter.rb
Your own username will show up in the place of username, and the creation date will naturally be different, but otherwise the output will be almost identical. The first part of the line is the revelant part. The letters r and w specify read and write permissions.
We're going to add execute permissions which will appear as an x in that line. To add execute permissions, run the following command.
chmod 755 greeter.rb
Now, if you check the file permissions again with ls -l greeter.rb, the output should be a little different.
-rwxr-xr-x 1 username staff 13 Feb 16 21:20 greeter.rb
The presence of x indicates that the file can be run directly without calling Ruby first. The following command should get our file to say "hello."
./greeter.rb
Almost there. Now, we just need to get rid of the prefix ./, which tells Bash where to look for greeter.rb, i.e., in the current directory. Before we complete this last step, though, let's rename our file to just greeter.
mv greeter.rb greeter
Now, for the last step. Everytime we call a Bash program, e.g., ls, chmod, mv, etc., Bash searches through a predefined list of folders looking for those programs. This is called the path. To see what the path is set to on your computer, try:
echo "$PATH"
The output should be a long string of various system-critical folders. We need to put our application into one of these folders. Traditionally, it's best to leave folders like /usr/bin/ and /bin/ alone. Instead, any kind of user additions should be placed in /usr/local/bin/. If that folder doesn't exist, create it with:
mkdir -p /usr/local/bin/
Now, we can either move our greeter into that folder, or leave the application where it is and just create a softlink (or an alias in OS X terms) within the /usr/local/bin/ folder. To create an alias, we'll use the ln command. From the directory where greeter lives, type:
ln -s $PWD/greeter /usr/local/bin/
Note that the $PWD variable will expand to an absolute path to our greeter script. Now, we're done and we can simply type greeter to invoke our Ruby script!
As a footnote, if any of the above Bash commands seem confusing, trying looking up their man page by typing man <command>.

Is it possible to override hashbang/shebang path behavior

I have a bunch of scripts (which can't be modified) written on Windows. Windows allows relative paths in its #! commands. We are trying to run these scripts on Unix but Bash only seems to respect absolute paths in its #! directives. I've looked around but haven't been able to locate an option in Bash or a program designed to replace and interpreter name. Is it possible to override that functionality -- perhaps even by using a different shell?
Typically you can just specify the binary to execute the script, which will cause the #! to be ignored. So, if you have a Python script that looks like:
#!..\bin\python2.6
# code would be here.
On Unix/Linux you can just say:
prompt$ python2.6 <scriptfile>
And it'll execute using the command line binary. I view the hashbang line as one which asks the operating system to use the binary specified on the line, but you can override it by not executing the script as a normal executable.
Worst case you could write some wrapper scripts that would explicitly tell the interpreter to execute the code in the script file for all the platforms that you'd be using.

Resources