Permalink
Browse files

Implement the &> and &>> redirects (from bash).

Fixes issue #90.
  • Loading branch information...
Andy Chu
Andy Chu committed May 1, 2018
1 parent ce1dda0 commit 8809c92ecfe758ec93a3d8e90e42492bd3061253
Showing with 43 additions and 6 deletions.
  1. +2 −0 core/id_kind.py
  2. +28 −4 core/process.py
  3. +2 −0 osh/lex.py
  4. +6 −0 osh/meta.py
  5. +4 −1 spec/redirect.test.sh
  6. +1 −1 test/spec.sh
View
@@ -187,6 +187,8 @@ def AddKinds(spec):
'DLessDash', # <<- here doc redirect for tabs?
'LessGreat', # <>
'Clobber', # >| POSIX?
'AndGreat', # bash &> stdout/stderr to file
'AndDGreat', # bash &>> stdout/stderr append to file
])
# NOTE: This is for left/right WORDS only. (( is not a word so it doesn't
View
@@ -69,8 +69,12 @@ def Open(self, path):
return os.fdopen(new_fd)
def _PushDup(self, fd1, fd2):
"""
Save fd2 and dup fd1 onto fd2.
"""Save fd2, and dup fd1 onto fd2.
Mutates self.cur_frame.saved.
Returns:
success Bool
"""
#log('---- _PushDup %s %s', fd1, fd2)
need_restore = True
@@ -117,13 +121,13 @@ def _ApplyRedirect(self, r, waiter):
ok = True
if r.tag == redirect_e.PathRedirect:
if r.op_id == Id.Redir_Great: # >
if r.op_id in (Id.Redir_Great, Id.Redir_AndGreat): # > &>
# NOTE: This is different than >| because it respects noclobber, but
# that option is almost never used. See test/wild.sh.
mode = os.O_CREAT | os.O_WRONLY | os.O_TRUNC
elif r.op_id == Id.Redir_Clobber: # >|
mode = os.O_CREAT | os.O_WRONLY | os.O_TRUNC
elif r.op_id == Id.Redir_DGreat: # >>
elif r.op_id in (Id.Redir_DGreat, Id.Redir_AndDGreat): # >> &>>
mode = os.O_CREAT | os.O_WRONLY | os.O_APPEND
elif r.op_id == Id.Redir_Less: # <
mode = os.O_RDONLY
@@ -137,9 +141,29 @@ def _ApplyRedirect(self, r, waiter):
util.error("Can't open %r: %s", r.filename, os.strerror(e.errno))
return False
# Apply redirect
if not self._PushDup(target_fd, r.fd):
ok = False
# Now handle the extra redirects for aliases &> and &>>.
#
# We can rewrite
# stdout_stderr.py &> out-err.txt
# as
# stdout_stderr.py > out-err.txt 2>&1
#
# And rewrite
# stdout_stderr.py 3&> out-err.txt
# as
# stdout_stderr.py 3> out-err.txt 2>&3
if ok:
if r.op_id == Id.Redir_AndGreat:
if not self._PushDup(r.fd, 2):
ok = False
elif r.op_id == Id.Redir_AndDGreat:
if not self._PushDup(r.fd, 2):
ok = False
os.close(target_fd) # We already made a copy of it.
# I don't think we need to close(0) because it will be restored from its
# saved position (10), which closes it.
View
@@ -240,6 +240,8 @@
R(r'[0-9]*<&', Id.Redir_LessAnd),
R(r'[0-9]*<>', Id.Redir_LessGreat),
R(r'[0-9]*>\|', Id.Redir_Clobber),
R(r'[0-9]*&>', Id.Redir_AndGreat),
R(r'[0-9]*&>>', Id.Redir_AndDGreat),
R(r'[^\0]', Id.Lit_Other), # any other single char is a literal
]
View
@@ -174,6 +174,9 @@ def IdInstance(i):
Id.Redir_DGreat: 1,
Id.Redir_Clobber: 1,
Id.Redir_LessGreat: 1, # TODO: What does echo <>foo do?
# bash &> and &>>
Id.Redir_AndGreat: 1,
Id.Redir_AndDGreat: 1,
# descriptor
Id.Redir_GreatAnd: 1, # echo >&2 means echo 1>&2
@@ -195,6 +198,9 @@ def IdInstance(i):
Id.Redir_DGreat: redir_arg_type_e.Path,
Id.Redir_Clobber: redir_arg_type_e.Path,
Id.Redir_LessGreat: redir_arg_type_e.Path,
# bash &> and &>>
Id.Redir_AndGreat: redir_arg_type_e.Path,
Id.Redir_AndDGreat: redir_arg_type_e.Path,
# descriptor
Id.Redir_GreatAnd: redir_arg_type_e.Desc,
View
@@ -253,7 +253,10 @@ stdout_stderr.py &> $TMP/f.txt
# order is indeterminate
grep STDOUT $TMP/f.txt >/dev/null && echo 'ok'
grep STDERR $TMP/f.txt >/dev/null && echo 'ok'
# stdout-json: "ok\nok\n"
# STDOUT:
ok
ok
# END
# N-I dash stdout: STDOUT
# N-I dash stderr: STDERR
# N-I dash status: 1
View
@@ -378,7 +378,7 @@ here-doc() {
}
redirect() {
sh-spec spec/redirect.test.sh --osh-failures-allowed 7 \
sh-spec spec/redirect.test.sh --osh-failures-allowed 5 \
${REF_SHELLS[@]} $OSH "$@"
}

0 comments on commit 8809c92

Please sign in to comment.