Is there a way to implement the following using 'for' in KornShell (ksh)?
Here is the C equivalent:
for(i=1;i<20;i++)
{
printf("%d",i);
}
I was wondering if this can be implemented using just 'for' and not 'while'
I tried the following, it does not seem to work.
for i in [1-20]
do
print $i
done
Please let me know your ideas and solutions.
Not really an answer, but an FYI to casual ksh users.
Edit 2019-05-12 (Minor edits below in bold, other info is now stricken).
To clarify on several comments here, there are 2 ksh's available in typical vendor installations (non-Linux (maybe them too?)).
Solaris and AIX have a ksh and ksh93 (probably true for other vendors too). The base ksh is also known as ksh88. Ksh93 is described in The New Kornshell Command and Programming Language, 1995
Linux systems that have a true ksh (not pdksh), mostly use ksh93 named as ksh.
Finally, to further confuse things, don't let the 1995 pub date trick you, ksh continues under was under active development by David Korn and Glen Fowler at AT&T until 2012?. Versions were released 2-3X per year. Some Linux versions pick up the newer versions.
These newer versions have very advanced features
(most of this taken from AT&T research UWIN page. search for the link 'notes and changes' (dead link) )
compound variables composed like c structs (no c datatypes, just typeset decls) (one user claims a 500 Meg in-memory struct)
Double precision floating point arithmetic with full C99 arithmetic ..The numbers Inf and NaN can be used in arithmetic expressions.
TAB-TAB completion generates a numbered list of completions ...
Support for processing/handling multibyte locales (e.g., en_US.UTF-8, hi_IN.UTF-8, ja_JP.eucJP, zh_CN.GB18030, zh_TW.BIG5 etc.) ...
/dev/(tcp|udp|sctp)/host/sevrice now handles IPv6 addresses ...
... seek on a file by offset or content with new redirection operators.
A new --showme option which allows portions of a script to behave as if -x were specified while other parts execute as usual. ...
The [[...]] operator =~ has been added which compares the string to an extended regular expression ....
The printf(1) builtin has been extended to support the = flag for centering a field ... (and others) ...
view-pathing
"Most of the utilities were developed by AT&T and conform to POSIX.2 and X/Open."
(note that ...s in above, usually indicate some qualifying information removed)
Korn and Fowler had also produced an advanced environment, UWIN (Unix for Windows) for people that use systems like Mingw or Cygwin, that would be worthy of a separate post. The downside for UWIN is,
not same set of utilities as you find in your favorite Linux.
Another file compilation environment that pretty much has to use MS Visual C (gcc support via Mingw is said to be on-the-way),
a very small support community,
the AT&T Common Public License V 1.0 Eclipse Public License* is not GNU.
See UWin main page (dead link) : unfortunately out of date, better to nose around in the dnld link above. Hmm, this is much better Glenn Fowler's FAQ for UWin (also dead, Time Machine anyone?).
I hope this helps!
Edit 2019-05-12 . The reason for the dead links?
David Korn and Glen Fowler Laid Off (at AT&T, 2012?
Information later surfaced that they are working at Google. I can't confirm this, so consider it as an old rumor.
AND see Is Ksh93 dead?
There still seems to be some activity at the ast git-hub site . ast is the over-arching package that includes ksh93. You can get the fresh source code there and compile it.
Here is the text of the project description. (There is considerably more information in the README.md).
KSH93
This repository contains the AT&T Software Technology (AST) toolkit
from AT&T Research. As of November 2017 the development focus has been
shifted to the ksh (or ksh93) command and supporting code required to
build it.
The non-ksh code of the AST project is no longer being actively
maintained. If you are interested in the non-ksh code see below for
details on which branches contain the full AST code base.
The project only supports systems where the compiler and underlying
hardware is ASCII compatible. This includes Linux on IBM zSeries but
not z/OS. The nascent, incomplete, support for EBCDIC has been
removed. See issue #742.
*
The EPL replaced AT&T's original CPL.
Unfortunately, it looks as if ksh does not have support for range-based brace expansion or support the (( )) construct so to do this compactly, you'll need to call the external binary seq like so:
for i in $(seq 1 20); do
echo $i
done
ksh93 supports the C-like (( ...;...; ...)):
for ((i=1;i<20;i+=1)); do
printf "%d " $i
done && print
This will produce:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
Heck, even the old syntax (using '{' ... '}' instead of 'do ... done' will work):
for((i=1;i<20;i+=1))
{
printf "%d " $i
} && print
In older shells, you can still get the same effect with
i=1 && while ((i<20)); do
printf "%d " $i
((i+=1))
done && print
ksh93 offers braceexpansion also if "braceexpand" is "on". Check with "set -o" and then use curly braces {}
for i in {1..20}
do
print $i
done
Related
I am working on a very simple bash script and I'm having a problem with understating why deprecated $[] is working flawlessly, while $(()) seems to break the whole thing.
The code I'm referring to is:
for i in {1..10};
do
printf %4d $[{1..10}*i]
echo
done
In this version I am having no issues, yet I wouldn't like to use deprecated bash elements, that's why I wanted to switch to $(()).
Unfortunately, as soon as I change my code to:
printf %4d $(({1..10}*i))
I receive an error:
./script_bash.sh: line 8: {1..10}*i: syntax error: argument expected (error token is "{1..10}*i")
I'd be thankful for some help with this one...
Setting the way back machine for 1990.
Bash implemented the $[] syntax per POSIX P1003.2d9 (circa 1990), which was a draft of the released P1003.2-1992. In the two years between draft and standard, POSIX had instead settled on the ksh88 $(()) syntax and behaviors. Chet Ramey (bash maintainer) had this to say, back in 2012:
Bash... implemented $[...] because there was no other
syntax at the time, and to gain some operational experience with
arithmetic expansion in the shell. Bash-1.14... lists both forms of arithmetic expansion, but by
the time bash-2.0 was released in 1995, the manual referred only to
the $((...)) form.
This suggests to me that the $[] form was experimental, and it had certain behaviors (like brace expansion) that were specified into oblivion when POSIX adopted the $(()) syntax. Those experimental behaviors were left in, since there were already scripts in the wild relying on them (remember more than 2 years had elapsed).
Chet makes clear in that same thread that the $[] form is obsolete, but not deprecated:
Now, it's hardly any problem to keep dragging the $[...] syntax along.
It takes only a few dozen bytes of code. I have no plans to remove it.
The current POSIX standard, C.2.6 Word Expansions > Arithmetic Expansion mentions the syntax (emphasis mine):
In early proposals, a form $[expression] was used. It was functionally equivalent to the "$(())" of the current text, but objections were lodged that the 1988 KornShell had already implemented "$(())" and there was no compelling reason to invent yet another syntax. Furthermore, the "$[]" syntax had a minor incompatibility involving the patterns in case statements.
So the as-implemented behavior in bash isn't quite to specification, but since there are no plans to remove it, I see no reason to forgo its benefits if it neatly solves your problem. However, as pointed out by #Barmar's comment, it'd be A Good Idea to comment the code and link it here so future developers know what the heck you mean!
$(()) is for arithmetic expressions, and brace expansion isn't done in arithmetic.
Make an array with a loop:
for i in {1..10}
do
vals=()
for j in {1..10}
do
vals+=($((i*j)))
done
printf "%4d" ${vals[#]}
done
printf %4d $(({1..10}*i))
does not work because of the order in which parameters are expanded in bash. The brace expansion ({}) is done earlier than arithmetic expansion ($(())) by bash. Your code would definitely work if it were other way around.
From man bash:
The order of expansions is: brace expansion; tilde expansion, parameter and variable expansion, arithmetic expansion, and command substitution (done in a left-to-right fashion); word splitting; and pathname expansion.
Ran across some bash like this. This question asks for explanation, rather than solution, but here would be a $(())-way of expressing this.
for i in {1..10}; do
printf %4d $(eval echo '$(('{1..10}'*i))')
echo
done
Brace expansion is inhibited inside an arithmetic expansion like it is for parameter expansion.
(bash manual)
To avoid conflicts with parameter expansion, the string ‘${’ is not
considered eligible for brace expansion, and inhibits brace expansion
until the closing ‘}’.
When installing my package, the user should at some point type
./wand-new "`cat wandcfg_install.spell`"
Or whatever the configuration file is called. If I put this line inside \code ... \endcode, doxygen thinks it is C++ or... Anyway, the word "new" is treated as keyword. How do I avoid this is in a semantically correct way?
I think \verbatim is disqualified because it actually is code, right?
(I guess the answer is to poke that Dimitri should add support for more languages inside a code block like LaTeX listings package, or at least add an disableparse option to code in the meantime)
Doxygen, as of July 2017, does not officially support documenting Shell/Bash scripting language, not even as an extension. There is an unofficial filter called bash-doxygen. Simple to setup: only one file download and three flags adjustments:
Edit the Doxyfile to map shell files to C parser: EXTENSION_MAPPING = sh=C
Set your shell script file names pattern as Doxygen inputs, like
e.g.: FILE_PATTERNS = *.sh
Mention doxygen-bash.sed in either the INTPUT_FILTER or the
FILTER_PATTERN directive of your Doxyfile. If doxygen-bash.sed is in
your $PATH, then you can just invoke it as is, else use sed -n -f /path/to/doxygen-bash.sed --.
Please note that since it uses C language parsing, some limitations apply, as stated in the main README page of bash-doxygen, one of them, at least in my tests, that the \code {.sh} recognises shell syntax, but all lines in the code block begin with an asterisk (*), apparently as a side-effect of requiring that all Doxygen doc sections have lines starting with double-hashes (##).
According to POSIX:
http://pubs.opengroup.org/onlinepubs/9699919799/utilities/sh.html
there are some cases where it not obvious. For example:
If the file is not in the current working directory,
the implementation may perform a search for an executable
file using the value of PATH, as described in Command Search and Execution.
My Bash 4.x doesn't follow this optional rule (due to security concern??) so I can't test how it be in real life...
What platform independent way to find directory of shell executable in shell script?
PS. Also dirname $0 case fail with:
#!/bin/sh
echo $0
dirname $0
when you:
$ sh runme.sh
runme.sh
.
So you need something like:
CMDPATH=`cd $(dirname $0); echo $PWD`
To made code dependent only on built-in shell capabilities I rewrite code to:
PREVPWD=$PWD
cd ${0%${0##*/}}.
CMDPATH=$PWD
cd $PREVPWD
This look ugly but doesn't require fork any executables...
EDIT3:
Though not strictly POSIX yet, realpath is a GNU core app since 2012. Full disclosure: never heard of it before I noticed it in the info coreutils TOC and immediately thought of this question, but using the following function as demonstrated should reliably, (soon POSIXLY?), and, I hope, efficiently
provide its caller with an absolutely sourced $0:
% _abs_0() {
> o1="${1%%/*}"; ${o1:="${1}"}; ${o1:=`realpath -s "${1}"`}; eval "$1=\${o1}";
> }
% _abs_0 ${abs0:="${0}"} ; printf %s\\n "${abs0}"
/no/more/dots/in/your/path2.sh
EDIT4: It may be worth highlighting that this solution uses POSIX parameter expansion to first check if the path actually needs expanding and resolving at all before attempting to do so. This should return an absolutely sourced $0via a messenger variable (with the notable exception that -s will preserve symlinks) as efficiently as I could imagine it could be done whether or not the path is already absolute.
EDIT2:
Now I believe I understand your problem much better which, unfortunately, actually renders most of the below irrelevant.
(minor edit: before finding realpath in the docs, I had at least pared down my version of this not to depend on the time field, but, fair warning, after testing some I'm less convinced ps is fully reliable in its command path expansion capacity)
On the other hand, you could do this:
ps ww -fp $$ | grep -Eo '/[^:]*'"${0#*/}"
eval "abs0=${`ps ww -fp $$ | grep -Eo ' /'`#?}"
I need to fix it to work better with fields instead of expecting the time field to come just before the process's path and relying on its included colon as a reference, especially because this will not work with a colon in your process's path, but that's trivial and will happen soon, I think. The functionality is otherwise POSIX compliant, I believe. Probably parameter expansion alone can do what is necessary, I think.
Not strictly relevant (or correct):
This should work in every case that conforms to POSIX guidelines:
echo ${0%/*}
EDIT:
So I'll confess that, at least at first blush, I don't fully understand the issue you describe. Obviously in your question you demonstrate some familiarity with POSIX standards for variable string manipulation via parameter expansion (even if your particular implementation seems slightly strained at a glance), so it's likely I'm missing some vital piece of information in my interpretation of your question and perhaps, at least in its current form, this is not the answer you seek.
I have posted before on parameter expansion for inline variable null/set tests which may or may not be of use to you as you can see at the "Portable Way to Check Emptiness of a Shell Variable" question. I mention this mainly because my answer there was in large part copied/pasted from the POSIX guidelines on parameter expansion, includes an anchored link to the guidelines coverage on this subject, and a few examples from both the canonical documentation and my own perhaps less expertly demonstrated constructs.
I will freely admit however, that while I do not yet fully understand what it is you ask, I don't believe that you will find a specific answer there. Instead I suspect you may have forgotten, as I do occasionally, that the # and % operators in POSIX string manipulation are used to specify the part of the string you want to remove, not that you wish to retain as some might find more intuitive. What I mean is any string slice you search for in this way is designed to disappear from your output, which will then be only what your remains of your original string after your specified search string is removed.
So here's a bit of an overview:
Whereas a single instance of either operator will remove only as little as possible to fully satisfy your search, but when doubly instanced the search is called in a greedy form and removes as much of the original string as your search could possibly allow.
Other than that you need only know some basic regex and remember that # begins its search for your removal string from the left and scans through to the right, and that % begins instead its search from the right and scans through to the left.
## short example before better learning if I'm on the right track
## demonstrating path manipulation with '#' and '%'
% _path_one='/one/two/three/four.five'
% _path_two='./four.five'
## short searching from the right with our wildcard to the right
## side of a single character removes everything to the right of
## of the specified character and the character itself
## this is a very simple means of stripping extensions and paths
% echo ${_path_one%.*} ${_path_one%/*}
/one/two/three/four /one/two/three
## long searching from the left with the wildcard to the left of
## course produces opposite results
% echo ${_path_one##*.} ${_path_one##*/}
five four.five
## will soon come back to show more probably
I believe you can get it using readlink:
scriptPath=$(readlink -f -- "$0")
scriptDirectory=${scriptPath%/*}
I'm facing the next problem in MinGW shell under windows. I have in my /etc/profile the expression:
export GIT_SSH="/c/Program Files/TortoiseGit/bin/TortoisePlink.exe"
This doesn't work when I use git fetch on the local repository. But if I do it like this (old DOS way), it works:
export GIT_SSH="/c/Progra~1/TortoiseGit/bin/TortoisePlink.exe"
My question is:
How can I make it work using spaces in the variable?
For testing purpose you can simulate something like this (any example is good):
export VAR="/c/Program Files/TortoiseGit/bin/TortoisePlink.exe"
# and try to execute like this
$VAR
Is there a solution for this (other than the previous mentioned)?
Execute it like this: "$VAR". This is one of the most significant gotchas in shell scripting because strings are always substituted literally and any contained spaces are treated as token delimiters rather than as characters of the string. Think of substituting a variable as a kind of code pasting at runtime.
What really happens when you write $VAR is that the shell tries to execute the binary /c/Program with a first argument Files/TortoiseGit/bin/TortoisePlink.exe.
I learned this the hard way by getting a strange syntax error in a big shell script for a particular input. No other languages I can think of can complain for syntax errors if the runtime input contains special characters - but that is the nature of shell scripting since command interpreters like bash and sh interpret the code line by line.
Whenever you expect a string to contain spaces and you don't want to treat it as separate tokens, enclose it in double quotes.
For reference, I solved a similar issue on osx by encapsulating the argument with escaped quotations. This may not be the best solution, but it seems to work.
alias sub="\"/Applications/Sublime Text 2.app/Contents/SharedSupport/bin/subl\""
I've solved it by including a backslash to escape the space:
/Program Files becomes /Program\ Files
Example:
export GIT_SSH=/c/Program\ Files/TortoiseGit/bin/TortoisePlink.exe
With Git 2.23 (Q3 2019, eight years later), a GIT_SSH set to /c/Program\ Files/TortoiseGit/bin/TortoisePlink.exe will... work (for those still on Windows 7)!
See commit eb7c786 (16 Jul 2019) by Johannes Schindelin (dscho).
(Merged by Junio C Hamano -- gitster -- in commit a5194d8, 25 Jul 2019)
mingw: support spawning programs containing spaces in their names
On some older Windows versions (e.g. Windows 7), the CreateProcessW() function does not really support spaces in its first argument, lpApplicationName.
But it supports passing NULL as lpApplicationName, which makes it figure out the application from the (possibly quoted) first argument of lpCommandLine.
Let's use that trick (if we are certain that the first argument matches
the executable's path) to support launching programs whose path contains
spaces.
This fixes git-for-windows/git issue 692
Git 2.24 (Q4 2019) adds a test:
See commit 71f4960 (01 Oct 2019) by Alexandr Miloslavskiy (SyntevoAlex).
(Merged by Junio C Hamano -- gitster -- in commit 424663d, 09 Oct 2019)
t0061: fix test for argv[0] with spaces (MINGW only)
The test was originally designed for the case where user reported that setting GIT_SSH to a .bat file with spaces in path fails on Windows: git-for-windows#692
some dirty hack for commands with spaces in variables -
for i in `k get po --all-namespaces -o wide | grep 'CrashLoop\|ImagePull' | awk '{printf " -n#%s#scale#deployment/%s",$1,$2}'`; do $(echo "kubectl ${i%-*-*} --replicas=0" | sed 's/#/ /g'); done
so here i use # instead of space forming variable and use $(echo variavle | sed 's/#/ /g') to execute lines from sed with spaces
It is hackk but it is simplier than all "corect ways" and works for me
Looking for an easy way to make a random number, i came across a web page that used the follow snip of code
echo $[ ( $RANDOM % $DIE_SIDES ) + 1 ]
What is the purpose of the $[. Googling did not reveal the answer I seek. Thanks
The $[expression] construct is used for arithmetic; $[1+1], for example, returns 2. You can also say $((expression)) or expr 1 + 1. The expr command version is old-school and should work in any shell, the $[expression] and $((expression)) versions work in bash but I'm not sure if they're covered by POSIX.
Update: The $[expression] form is a bash extension, the $((expression)) form is specified for the POSIX shell.