def Export(argv, mem): arg, i = EXPORT_SPEC.Parse(argv) if arg.n: for name in argv[i:]: m = match.IsValidVarName(name) if not m: raise args.UsageError('export: Invalid variable name %r' % name) # NOTE: bash doesn't care if it wasn't found. mem.ClearFlag(name, var_flags_e.Exported, scope_e.Dynamic) else: for arg in argv[i:]: parts = arg.split('=', 1) if len(parts) == 1: name = parts[0] val = None # Creates an empty variable else: name, s = parts val = runtime.Str(s) m = match.IsValidVarName(name) if not m: raise args.UsageError('export: Invalid variable name %r' % name) #log('%s %s', name, val) mem.SetVar(runtime.LhsName(name), val, (var_flags_e.Exported, ), scope_e.Dynamic) return 0
def _ParseForEachLoop(self): node = ast.ForEach() node.do_arg_iter = False ok, iter_name, quoted = word.StaticEval(self.cur_word) if not ok or quoted: self.AddErrorContext( "Invalid for loop variable", word=self.cur_word) return None if not match.IsValidVarName(iter_name): self.AddErrorContext( "Invalid for loop variable name", word=self.cur_word) return None node.iter_name = iter_name self._Next() # skip past name if not self._NewlineOk(): return None in_spid = const.NO_INTEGER semi_spid = const.NO_INTEGER if not self._Peek(): return None if self.c_id == Id.KW_In: self._Next() # skip in in_spid = word.LeftMostSpanForWord(self.cur_word) + 1 x = self.ParseForWords() if x is None: return None iter_words, semi_spid = x words2 = braces.BraceDetectAll(iter_words) words3 = word.TildeDetectAll(words2) if iter_words is None: # empty list of words is OK return None node.iter_words = words3 elif self.c_id == Id.Op_Semi: node.do_arg_iter = True # implicit for loop self._Next() elif self.c_id == Id.KW_Do: node.do_arg_iter = True # implicit for loop # do not advance else: self.AddErrorContext("Unexpected word in for loop: %s", self.cur_word, word=self.cur_word) return None node.spids.extend((in_spid, semi_spid)) body_node = self.ParseDoGroup() if not body_node: return None node.body = body_node return node
def Repr(argv, mem): """Given a list of variable names, print their values. 'repr a' is a lot easier to type than 'argv.py "${a[@]}"'. """ status = 0 for name in argv: if not match.IsValidVarName(name): util.error('%r is not a valid variable name', name) return 1 # TODO: Should we print flags too? val = mem.GetVar(name) if val.tag == value_e.Undef: print('%r is not defined' % name) status = 1 else: print('%s = %s' % (name, val)) return status
def _ApplyPrefixOp(self, val, op_id): """ Returns: value """ assert val.tag != value_e.Undef if op_id == Id.VSub_Pound: # LENGTH if val.tag == value_e.Str: # NOTE: Whether bash counts bytes or chars is affected by LANG # environment variables. # Should we respect that, or another way to select? set -o # count-bytes? # https://stackoverflow.com/questions/17368067/length-of-string-in-bash try: length = libstr.CountUtf8Chars(val.s) except util.InvalidUtf8 as e: # TODO: Add location info from 'part'? Only the caller has it. if self.exec_opts.strict_word_eval: raise else: # NOTE: Doesn't make the command exit with 1; it just returns a # length of -1. util.warn(e.UserErrorString()) return runtime.Str('-1') elif val.tag == value_e.StrArray: # There can be empty placeholder values in the array. length = sum(1 for s in val.strs if s is not None) return runtime.Str(str(length)) elif op_id == Id.VSub_Bang: # NOTES: # - Could translate to eval('$' + name) or eval("\$$name") # - ${!array[@]} means something completely different. TODO: implement # that. # - It might make sense to suggest implementing this with associative # arrays? # Treat the value of the variable as a variable name. if val.tag == value_e.Str: try: # e.g. ${!OPTIND} gives $1 when OPTIND is 1 arg_num = int(val.s) return self.mem.GetArgNum(arg_num) except ValueError: if not match.IsValidVarName(val.s): # TODO: location information. # Also note that bash doesn't consider this fatal. It makes the # command exit with '1', but we don't have that ability yet? e_die('Bad variable name %r in var ref', val.s) return self.mem.GetVar(val.s) elif val.tag == value_e.StrArray: raise NotImplementedError( '${!a[@]}') # bash gets keys this way else: raise AssertionError else: raise AssertionError(op_id)
def _MakeAssignment(self, assign_kw, suffix_words): # First parse flags, e.g. -r -x -a -A. None of the flags have arguments. flags = [] n = len(suffix_words) i = 1 while i < n: w = suffix_words[i] ok, static_val, quoted = word.StaticEval(w) if not ok or quoted: break # can't statically evaluate if static_val.startswith('-'): flags.append(static_val) else: break # not a flag, rest are args i += 1 # Now parse bindings or variable names assignments = [] while i < n: w = suffix_words[i] left_spid = word.LeftMostSpanForWord(w) kov = word.LooksLikeAssignment(w) if kov: k, op, v = kov t = word.TildeDetect(v) if t: # t is an unevaluated word with TildeSubPart a = (k, op, t, left_spid) else: a = (k, op, v, left_spid) # v is unevaluated word else: # In aboriginal in variables/sources: export_if_blank does export "$1". # We should allow that. # Parse this differently then? # dynamic-export? # It sets global variables. ok, static_val, quoted = word.StaticEval(w) if not ok or quoted: self.AddErrorContext( 'Variable names must be constant strings, got %s', w, word=w) return None # No value is equivalent to '' if not match.IsValidVarName(static_val): self.AddErrorContext('Invalid variable name %r', static_val, word=w) return None a = (static_val, assign_op_e.Equal, None, left_spid) assignments.append(a) i += 1 # TODO: Also make with LhsIndexedName pairs = [] for lhs, op, rhs, spid in assignments: p = ast.assign_pair(ast.LhsName(lhs), op, rhs) p.spids.append(spid) pairs.append(p) node = ast.Assignment(assign_kw, flags, pairs) return node