How to input a comment on csh? - comments

In bash, I used # to input comment. Even on interactive session.
bash-3.2$ #
bash-3.2$ #
bash-3.2$ #
bash-3.2$
csh spits error for this. How can I input some comment on interactive csh session? In other words, I am looking for a way to make 100% sure comment in csh.
root#freebsd9:~ # #
#: Command not found.
root#freebsd9:~ # # 3e
#: Command not found.
root#freebsd9:~ # #
#: Command not found.
root#freebsd9:~ #

Interactive csh or tcsh doesn't do comments. The # character introduces a comment only in a script. (This is unlike the behavior of sh and its derivatives, such as bash.) Quoting the csh man page (from Solaris 9, one of the remaining systems where csh is not just a symlink to tcsh):
When the shell's input is not a terminal, the character #
introduces a comment that continues to the end of the input line.
Its special meaning is suppressed when preceded by a \ or enclosed in
matching quotes.
The point, I think, is that interactive commands don't need comments.
If you're using tcsh, you can do something similar with the built-in : command, which does nothing:
% : 'This is not a comment, but it acts like one.'
(where % represents the shell prompt and : is the command). Quoting the argument is a good idea; otherwise, though the command is not executed, it can have some effect:
% : This will create the file "oops.txt" > oops.txt
Note that since : is a command, it must be followed by a space.
The : command was originally introduced in a very early version of the Bourne shell, or perhaps even before that.
However, the /bin/csh version of the : command does not permit any arguments, making it useless as a comment replacement:
csh% : 'This will not work.'
:: Too many arguments
csh%
(I didn't realize that when I initially posted this answer. I must have tested it with tcsh rather than a true csh.)
Since : doesn't work in pure csh, the next best solution is probably to use echo and redirect the output:
csh% echo 'This is not a comment, but it acts like one.' > /dev/null
Obligatory link: http://www.perl.com/doc/FMTEYEWTK/versus/csh.whynot

For the reference, I like to note my current workaround. It's using echo.
#!/bin/tcsh
echo "This is comment line."
echo "But still, beware... Because `expression` is still being evaluated.
This is the best way I could find.

To use # for interactive comments in tcsh or csh, this seems to work in most cases:
alias '#' 'echo \!* >> /dev/null'
It can be run by the user interactively or placed in a .tcshrc or .cshrc configuration file as appropriate.
(The alias can obviously be named differently as you might desire; normal restrictions apply.)
Note: as Keith Thompson noted above, this will still give errors if your comment includes redirect characters such as > or >>, but used this way does not appear to actually create an undesired redirect file. Surrounding the comment in single quotes (') is still a workaround.

Check if your script has UNIX EOL Format. Has some problem with Windows EOL Format.

Related

Read file and run command in zsh

So I generally create job files with a list of commands in it. Then I execute it like so
cat jobFile | while read a; do $a; done
Which always works in bash. However, I've just started working in Mac which apparently uses zsh. And this command fails with "no such file" etc. I've tested the job file by running few lines from it manually, so it should be fine.
I've found questions on zsh read inbut they tend to be reading in from variables e.g. $a=('a' 'b' 'c') or echo $a
Thank you for your answers!
In bash, unquoted parameter expansions always undergo word-splitting, so if a="foo bar", then $a expands to two words, foo and bar. As a command, this means running the command foo with an argument bar.
In zsh, parameter expansions to not undergo word-splitting by default, which means the same expansion $a would produce a single word foo bar, treated as the name of the command to execute.
In either case, relying on parameter expansion to "parse" a shell command is fragile; in addition to word-splitting, the expansion is subject to pathname expansion (globbing), and you are limited to simple commands and their arguments. No pipes, lists (&&, ||), or redirections allowed, as everything will be treated as the command name and a sequence of arguments.
What you want in both shells is to simply treat your job file as a shell script, which can be executed in the current shell using the . command:
. jobFile
Why are you executing it in such a cumbersome way? Assuming jobFile is a file holding a sequence of bash commands, you can simply run it as
bash jobFile
If it contains a sequence of zsh commands, you can likewise run it as
zsh jobFile
If you follow this approach, I would however reflect in the name of the job file, what shell it is intended for, i.e.
bash jobFile.bash
zsh jobFile.zsh
and, if you write a job file so that it is supposed to be compatible with either shell, I would name it jobFile.sh.

Space between # and ! in shebang (# !/usr/bin/ksh)

I am writing a Korn shell script that involves process substitution using < <(), like this:
array=()
while IFS= read -r -d '' x;do
array+=( "$x" )
done < <(some command)
This is trying to insert into array all string returned by some command. The curious thing is that this works when my shebang looks like this:
# !/usr/bin/ksh
which is of course unusual (notice the space between # and !). On the other hand, when my shebang looks like #!/usr/bin/ksh (the right way, apparently), this script fails with the error syntax error: '< ' unexpected. Why is this? What difference does having a space in the shebang mean? Google gave me several answers saying that a space between !# and !/usr... is okay, but nothing regarding a space between ! and #.
# ! is an invalid shebang, and entirely ignored. Behavior of a script with no shebang depends on how you invoke it.
If invoked from a shell: Some shells use /bin/sh to run such scripts; others use themselves for the purpose. Presumably the shell you're interactively using when testing this (and finding the script to work only with an invalid shebang) is in the latter set, so your script is actually being run with bash, or otherwise your active interactive shell at the time.
If invoked without a shell: Most operating systems will refuse to execute such a binary.
Real David Korn ksh93 supports process substitution correctly, but some 3rd-party clones and ancient ksh implementations don't.
If you're going to use ksh, using genuine David Korn ksh93 (not mksh, pdksh, or another 3rd-party clone) is strongly preferred, and (to your immediate point) will ensure process substitution support.

How do I redirect output when the command to execute is stored in a variable in a bash script?

Consider the following script:
#!/bin/bash
CMD="echo hello world > /tmp/hello.out"
${CMD}
The output for this is:
hello world > /tmp/hello.out
How can I modify CMD so that the output gets redirected to hello.out?
For my use case, it is not feasible to either do this:
${CMD} > /tmp/hello.out
or to add this at the top of the script:
exec > /tmp/hello.out
No, there is no way to make a redirection happen from a variable.
Why?
The first thing the shell does with a command line is:
Each line that the shell reads from the standard input or a script is called a pipeline; it contains one or more commands separated by zero or
more pipe characters (|). For each pipeline it reads, the shell breaks it up into commands, sets up the I/O for the pipeline, then does the following for each command (Figure 7-1):
From: Learning the bash Shell Unix Shell Programming . Chapter Preview / Figure . Pdf
That means that even before starting with the first word of a command line, the redirections are set up.
The "Parameter Expansion" happens quite a lot latter (in step 6 of the Figure).
There is no way to set up redirections after a variable is expanded.
Unless ...
The "command line is reprocessed" using eval.
eval "$CMD"
But this comes with a lot of danger.
The command line is changed by the first processing in the 12 steps detailed in the book (quotes are removed, variables expanded, words split, etc.).
It is usually quite difficult to estimate all the changes and consequences before the line is actually processed.
And then, it is processed again.
You can use eval to instruct the shell to reinterpret the variable content as a shell command:
eval $CMD

How can I find the path to a script being sourced in dash?

I am trying to have a sourced shell script determine its own location, and I have found that this is a difficult task for dash.
In bash, sh, and csh, I can use: $_.
In fish, I can use (status -f).
In dash, I have had no luck...
I have tried sourcing the path.sh file shown below with the following results:
# path.sh
called=$_
echo called: $called
echo underscore: $_
echo zero: $0
echo dash_source: $DASH_SOURCE
echo bash_source: $BASH_SOURCE
dash -c ". path.sh"
outputs:
called: /usr/local/bin/dash
underscore: /usr/local/bin/dash
zero: dash
dash_source:
bash_source:
How can I get the path to path.sh in dash?
There does not appear to be any portable (POSIX-standard) way to do this.
POSIX refers to sourcing as "dot scripts." While several other parts of the shell language reference do discuss "dot scripts," none of these instances appear to provide any way to find out the path to the currently-executing dot script. In particular, $0 is reserved for "shell scripts" which are a different thing from "dot scripts." The word "source" does not appear on the page in any capitalization or as part of any larger word (so no $BASH_SOURCE-like thing). None of the standard environment variables which affect the shell seem relevant either. I'm going to say this is not possible within the POSIX spec. Since Dash follows POSIX quite closely, it is unlikely to have a Dashism for this specific case (if it was going to do that, it would've borrowed $BASH_SOURCE or created an analogous variable).
I agree with the accepted answer, though I was able to make it work using lsof:
x=$(lsof -p $$ -Fn0 | tail -1); script=${x#n}
This works because dash keeps the script open when it is executing. This needs to be the first line of the script, otherwise it may fail if dash later opens additional file descriptors.
This is not possible under POSIX shell standard (which dash implements, nothing more). . is a built-in, not a shell script, therefore, the $0 positional argument refers to the caller of ., and $1 refers to an argument to that caller, if one exists. In particular, none of these things refer to the . itself or its argument script.
If you can change the code that sources, put this function before all source calls.
.() {
command . "$1"
}
. script.sh
# more dot scripts
what is does it to hook the 'dot script' function, now inside the function, positional parameters are the function parameters.
So inside your sourced script, positional parameters are:
. # $0
script.sh # $1
Inside the script being sourced:
# script.sh
echo "$(basename "$1")" # this is the name of the script itself

ZSH/Shell variable assignment/usage

I use ZSH for my terminal shell, and whilst I've written several functions to automate specific tasks, I've never really attempted anything that requires the functionality I'm after at the moment.
I've recently re-written a blog using Jekyll and I want to automate the production of blog posts and finally the uploading of the newly produced files to my server using something like scp.
I'm slightly confused about the variable bindings/usage in ZSH; for example:
DATE= date +'20%y-%m-%d'
echo $DATE
correctly outputs 2011-08-23 as I'd expect.
But when I try:
DATE= date +'20%y-%m-%d'
FILE= "~/path/to/_posts/$DATE-$1.markdown"
echo $FILE
It outputs:
2011-08-23
blog.sh: line 4: ~/path/to/_posts/-.markdown: No such file or directory
And when run with what I'd be wanting the blog title to be (ignoring the fact the string needs to be manipulated to make it more url friendly and that the route path/to doesn't exist)
i.e. blog "blog title", outputs:
2011-08-23
blog.sh: line 4: ~/path/to/_posts/-blog title.markdown: No such file or directory
Why is $DATE printing above the call to print $FILE rather than the string being included in $FILE?
Two things are going wrong here.
Firstly, your first snippet is not doing what I think you think it is. Try removing the second line, the echo. It still prints the date, right? Because this:
DATE= date +'20%y-%m-%d'
Is not a variable assignment - it's an invocation of date with an auxiliary environment variable (the general syntax is VAR_NAME=VAR_VALUE COMMAND). You mean this:
DATE=$(date +'20%y-%m-%d')
Your second snippet will still fail, but differently. Again, you're using the invoke-with-environment syntax instead of assignment. You mean:
# note the lack of a space after the equals sign
FILE="~/path/to/_posts/$DATE-$1.markdown"
I think that should do the trick.
Disclaimer
While I know bash very well, I only started using zsh recently; there may be zshisms at work here that I'm not aware of.
Learn about what a shell calls 'expansion'. There are several kinds, performed in a particular order:
The order of word expansion is as follows:
tilde expansion
parameter expansion
command substitution
arithmetic expansion
pathname expansion, unless set -f is in effect
quote removal, always performed last
Note that tilde expansion is only performed when the tilde is not quoted; viz.:
$ FILE="~/.zshrc"
$ echo $FILE
~/.zshrc
$ FILE=~./zshrc
$ echo $FILE
/home/user42/.zshrc
And there must be no spaces around the = in variable assignments.
Since you asked in a comment where to learn shell programming, there are several options:
Read the shell's manual page man zsh
Read the specification of the POSIX shell, http://pubs.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html, especially if you want to run your scripts on different operating systems (and you will find yourself in that situation one fine day!)
Read books about shell programming.
Hang out in the usenet newsgroup comp.unix.shell where a lot of shell wizards answer questions

Resources