C: List aliases of current shell (not subshell!) - c

I'm trying to find all aliases that the current shell possesses (in a C program). I've tried system("alias"), popen("alias", "r") and execvp("alias", ...), the latter of which doesn't work at all (because alias is a shell-specific command) and the first two of which run that command in a subshell (which is sh and not bash) -> there, aliases are disabled because they are defined in my ~/.bashrc. Executing bash and reading the output of alias isn't possible either because bash will only go to the alias definitions if it is in interactive mode.
If I do run bash in interactive mode, I get a huge delay time and a prompt output that I want to avoid.
Basically, what I want, is to have similar behaviour as time(1). It looks up current aliases without even executing any command! (it will fork only once, that is, for the passed command)
Crawling the Internet was to no avail.
Question: How do I look up all aliases in the current shell? Will there be any portability issues? If yes, how to avoid them?
Regards.

You can't. time is a built-in and it can access the aliases which are stored internally to the shell instance that is running. if you need to work out what the shell will execute, you need to run which or something similar.
time isn't doing anything clever or secret. It's just a prefix to the command to make the shell print out some timing information.

I just downloaded and built GNU time on my Mac. To my surprise and chagrin, it doesn't read aliases from the parent bash, whereas the built-in time does.
$ alias frodo=ls
$ ./time frodo
./time: cannot run frodo: No such file or directory
Command exited with non-zero status 127
0.00user 0.00system 0:00.00elapsed ?%CPU (0avgtext+0avgdata 819200maxresident)k
0inputs+0outputs (0major+59minor)pagefaults 0swaps
$ time frodo
AUTHORS INSTALL Makefile.in config.cache configure* error.o getopt.o getpagesize.h mkinstalldirs* resuse.h stamp-vti time.c time.texi version.texi
COPYING Makefile NEWS config.log configure.in getopt.c getopt1.c install-sh* port.h resuse.o texinfo.tex time.info version.c wait.h
ChangeLog Makefile.am README config.status* error.c getopt.h getopt1.o mdate-sh* resuse.c stamp-v time* time.o version.o
real 0m0.005s
user 0m0.002s
sys 0m0.002s
$
I suspect there's something undocumented is passing under the table to help the builtin time see aliases (or BSD provides something that GNU time won't or can't make use of).
EDIT: Prompted by Tom Tanner's answer (+1 Tom), I just realised that time frodo doesn't invoke /usr/bin/time (as suggested by which time), and that explicitly running /usr/bin/time frodo also fails, so it must be that entering time by itself invokes a bash builtin command.

Related

How do you execute a bash script with "system()" when there are spaces in the file path?

I created a simple bash script called "myscript.h" I gave it a .h extensions for reasons that I won't disclose here. This bash script lives in "/var/ftp/something with spaces".
From the terminal, I can type in "/var/ftp/something with spaces/myscript.h" and the script works perfectly.
However, from within my C program, I type in
system("/var/ftp/something with spaces/myscript.h")
and it complains that "/var/ftp/something" is not found. I've changed my system call to the following with forward slashes:
system("/var/ftp/something\ with\ spaces/myscript.h")
However, it still complains that "/var/ftp/something" is not found. Assuming I can't change the directory names, how can I get around this?
Thanks!
To run a single script, you might avoid the system(3) library function (and use lower level system calls like fork(2), execve(2), waitpid(2)... which are used by the implementation of system(3)), or you could quote the script name when passing it to system(3).
For more details, read Advanced Linux Programming.
On Linux, system(3) is documented to fork a /bin/sh -c process. See sh(1p). And that POSIX shell has some quoting rules. You might use double-quotes and backslashes appropriately. So you would construct (and perhaps check) the string passed to system(3) (perhaps using asprintf(3) or snprintf(3) with care). Be aware that the C compiler also has (different) quoting conventions for string literals.
In general, you should avoid code injection (imagine a naughty user giving some a; rm -rf $HOME &; input as a "directory" name; you don't want to run system(3) on the weird "/var/ftp/a; rm -rf $HOME &;/myscript.h" string)
In your particular case, I recommend using fork(2), execve(2) (perhaps thru some carefully choosen exec(3) function), waitpid(2)... appropriately. This has the slight advantage to avoid depending on and running /bin/sh so could be slightly faster (by a millisecond).
Understand more the role of an Unix shell; for example, read about the various shell expansions in bash (they are similar to those mandated by POSIX sh) and be aware of globbing. See glob(7)
Note that you're adding quotes when running from the shell. You need to do the same here. Add quotes to the path name you're sending to system:
system("\"/var/ftp/something with spaces/myscript.h\"")
This should work with gcc version 5.4.0
system("\'\'/var/ftp/something\\ with\\ spaces/myscript.h\'\'");
Just put the filename inside single quotes
system("rm '/var/ftp/something with spaces/myscript.h'")

C script doesn't works globaly

I write test script in C, compiled it and put to /usr/bin directory, to execute it from anywhere like mkdir or ls. But then I wrote $ test nothing happen, if i wrote $ /usr/bin/test it works well. What I did wrong? I got Ubuntu 14.
In shells (such as bash), there's a shell builtin named test. So, when you execute test, shell builtin test is executed even though you have test in /usr/bin/ assuming you have /usr/bin/ in your PATH. When you give full path, there's no ambiguity there (like other binaries namedtest which might be there in PATH or builtins).
You can test this by running:
$type test
Name your binary to something else that doesn't interfere with system binaries or shell builtins. It's generally recommended to put your binaries in designated locations such as usr/local/bin/ even if your binary is uniquely named.

How do I execute my program without ./a.out command?

I have written a c program. I want to pipe the program and I want to make it look meaningful. So instead of writing ./a.out each time, I want to name it changetext. To achieve that, I compiled my program following way: gcc -o changetext myprog.c. To the best of my knowledge, this should replace the use of ./a.out and changetext should do that instead. But I'm getting command not found. I am new to c and unix environment. Any suggestion appreciated.
As I said in a comment, you can either put a dot slash (./) in front of the executable to run it
./changetext
Or you put in in a directory that is referenced in the PATH environment variable. A nice explanation of this safety feature can be found here (thanks to rubenvb):
http://www.linfo.org/dot_slash.html
It says that this is more or less to distinguish built-in commands from user-written commands with the same name. I am not convinced though. The shell could simply prefer built-in names to user-supplied ones, and look in the current directory as well as in the PATH.
But this is the *nix way.
In order to compile and run a program such as your changetext with just the command chanhetext, you must put the binary in a directory listed in your PATH environment variable. It is recommended that you put programs that you made for your own use in the ~/bin/ directory. The command you would use to accomplish this would be the following, assuming ~/bin/ already exists:
gcc -o ~/bin/changetext myprog.c
If it does not exist, you can simply create it, then log out and back in.
If you are tired of doing the ./ before the program name you can always make an alias such as
alias a='./a.out' or alias changetext='./changetext'
this just basically look for everytime you type changetext or a and then replaces it to have the ./ infront of it

Custom completion in shell

I need to develop a utility that will take command line arguments as follows:
$ lsm -g <group> -t <type> -d <device>
My project manager wants that when we type any argument like lsm -g and press Tab, then a function will be called to run a database query and fetch help for the user about the value of the option.
Similar to how terminal behaves in this case:
$ cd <tab>
.bash_history .local/
.bash_logout .log-report.log.swp
.bash_profile .macromedia/
.bashrc .mozilla/
.cache/ Music/
.config/ .mysql_history
.dbus/ .nautilus/
Desktop/ .opera/
Documents/ .orc/
Downloads/ .p2/
.eclipse/ Pictures/
I have tried these approaches:
Use fork() to create a child to call the help function.
Use execv() to run a help function using a seperate binary.
But both require that lsm be running.
I'm not sure how to proceed further.
As various commentators have noted, tab-completion is implemented in the shell, not in the program which is about to be executed.
Most shells have frameworks for implementing custom tab-completion. In the case of bash, it is implemented with the help of the readline library. There is extensive documentation in the bash manual and there are a variety of tutorials kicking around the internet, such as this one from Debian (not an endorsement, just the result of a quick Google search).

C - program compiling, but unable to provide arguments

I'm on a Mac and in terminal I'm compiling my program
gcc -Wall -g -o example example.c
it compiles (there are no errors), but when I try to provide command line arguments
example 5 hello how are you
terminal responds with "-bash: example: command not found"
how am supposed to provide the arguments I want to provide after compiling?
Run it like this with path:
./example 5 hello how are you
Unless the directory where the example binary is part of the PATH variable, what you have won't work even if the binary you are running is in the current directory.
It is not a compilation issue, but an issue with your shell. The current directory is not in your PATH (look with echo $PATH and use which to find out how the shell uses it for some particular program, e.g. which gcc).
I suggest testing your program with an explicit file path for the program like
./example 5 hello how are you
You could perhaps edit your ~/.bashrc to add . at the end of your PATH. There are pro and conses (in particular some possible security issues if your current directory happens to be sometimes a "malicious" one like perhaps /tmp might be : bad guys might put there a gcc which is a symlink to /bin/rm so you need to add . at the end of your PATH if you do).
Don't forget to learn how to use a debugger (like gdb). This skill is essential when coding in C (or in C++). Perhaps consider also upgrading your gcc (Apple don"t like much its current GPLv3 license so don't distribute the recent one; try just gcc -v and notice that the latest released GCC is today 4.8.1).
./example 5 Hello how are you is the syntax you're looking for.
This article lends a good explanation as to why this is important.
Basically, when you hit Enter, the shell checks to see if the first set of characters is an absolute path. If it's not, it checks the PATH variable to find executables with the name of the command you are trying to run. If it's found, it will be run, but otherwise it will crash and burn and you will become very sad.

Resources