연산자
누셸은 일반적인 수학, 논리 및 문자열 연산을 위해 다음 연산자를 지원합니다.
연산자 | 설명 |
---|---|
+ | 더하기 |
- | 빼기 |
* | 곱하기 |
/ | 나누기 |
// | 정수/내림 나누기 |
mod | 모듈로 |
** | 거듭제곱 |
== | 같음 |
!= | 같지 않음 |
< | 작음 |
<= | 작거나 같음 |
> | 큼 |
>= | 크거나 같음 |
=~ 또는 like | 정규식 일치 / 문자열이 다른 문자열 포함 |
!~ 또는 not-like | 역 정규식 일치 / 문자열이 다른 문자열을 포함하지 않음 |
in | 목록에 값이 있음 |
not-in | 목록에 값이 없음 |
has | 목록에 값이 있음 |
not-has | 목록에 값이 없음 |
not | 논리 부정 |
and | 두 부울 표현식의 논리곱 (단락 평가) |
or | 두 부울 표현식의 논리합 (단락 평가) |
xor | 두 부울 표현식의 배타적 논리합 |
bit-or | 비트 OR |
bit-xor | 비트 XOR |
bit-and | 비트 AND |
bit-shl | 비트 왼쪽 시프트 |
bit-shr | 비트 오른쪽 시프트 |
starts-with | 문자열이 ~로 시작 |
ends-with | 문자열이 ~로 끝남 |
++ | 목록 추가 |
괄호는 평가 순서를 지정하기 위해 그룹화하거나 명령을 호출하고 표현식에서 결과를 사용하는 데 사용할 수 있습니다.
연산 순서
연산의 우선 순위를 이해하려면 help operators | sort-by precedence -r
명령을 실행할 수 있습니다.
우선 순위가 높은 순서대로 나열된 이 문서에서는 연산을 다음과 같이 자세히 설명합니다.
- 괄호 (
()
) - 거듭제곱/멱 (
**
) - 곱하기 (
*
), 나누기 (/
), 정수/내림 나누기 (//
), 모듈로 (mod
) - 더하기 (
+
) 및 빼기 (-
) - 비트 시프트 (
bit-shl
,bit-shr
) - 비교 연산 (
==
,!=
,<
,>
,<=
,>=
), 멤버십 테스트 (in
,not-in
,starts-with
,ends-with
), 정규식 일치 (=~
,!~
), 목록 추가 (++
) - 비트 AND (
bit-and
) - 비트 XOR (
bit-xor
) - 비트 OR (
bit-or
) - 논리 AND (
and
) - 논리 XOR (
xor
) - 논리 OR (
or
) - 할당 연산
- 논리 NOT (
not
)
3 * (1 + 2)
# => 9
유형
모든 연산이 모든 데이터 유형에 대해 의미가 있는 것은 아닙니다. 호환되지 않는 데이터 유형에 대해 연산을 시도하면 무엇이 잘못되었는지 설명하는 오류 메시지가 표시됩니다.
"spam" - 1
# => Error: nu::parser::unsupported_operation (link)
# =>
# => × Types mismatched for operation.
# => ╭─[entry #49:1:1]
# => 1 │ "spam" - 1
# => · ───┬── ┬ ┬
# => · │ │ ╰── int
# => · │ ╰── doesn't support these values.
# => · ╰── string
# => ╰────
# => help: Change string or int to be the right types and try again.
규칙이 때로는 약간 엄격하게 느껴질 수 있지만, 반면에 예기치 않은 부작용이 적을 것입니다.
정규식 / 문자열 포함 연산자
=~
및 !~
연산자는 정규식을 평가하는 편리한 방법을 제공합니다. 정규식을 몰라도 사용할 수 있습니다. 또한 한 문자열이 다른 문자열을 포함하는지 확인하는 쉬운 방법이기도 합니다.
string =~ pattern
은string
이pattern
과 일치하는 경우 true를 반환하고 그렇지 않으면 false를 반환합니다.string !~ pattern
은string
이pattern
과 일치하는 경우 false를 반환하고 그렇지 않으면 true를 반환합니다.
예시:
foobarbaz =~ bar # true 반환
foobarbaz !~ bar # false 반환
ls | where name =~ ^nu # 이름이 "nu"로 시작하는 모든 파일 반환
두 연산자 모두 Rust 정규식 크레이트의 is_match()
함수를 사용합니다.
대소문자 구분
연산자는 일반적으로 문자열에 대해 작동할 때 대소문자를 구분합니다. 대신 대소문자를 구분하지 않는 작업을 수행하는 몇 가지 방법이 있습니다.
- 정규식 연산자에서
(?i)
대소문자 구분 안 함 모드 수정자를 지정합니다.
"FOO" =~ "foo" # false 반환
"FOO" =~ "(?i)foo" # true 반환
str contains
명령의--ignore-case
플래그를 사용합니다.
"FOO" | str contains --ignore-case "foo"
- 비교하기 전에
str downcase
를 사용하여 문자열을 소문자로 변환합니다.
("FOO" | str downcase) == ("Foo" | str downcase)
스프레드 연산자
누셸에는 목록과 레코드를 풀기 위한 스프레드 연산자(...
)가 있습니다. 이전에 자바스크립트를 사용해 본 적이 있다면 익숙할 것입니다. 일부 언어에서는 스프레드/스플랫 연산자에 *
를 사용합니다. 여러 값이나 키-값 쌍이 예상되는 곳에서 목록이나 레코드를 확장할 수 있습니다.
스프레드 연산자를 사용할 수 있는 세 곳이 있습니다.
목록 리터럴에서
여러 목록을 함께 연결하고 싶지만 개별 값을 산재시키고 싶다고 가정해 보겠습니다. append
및 prepend
를 사용하여 이 작업을 수행할 수 있지만 스프레드 연산자를 사용하면 더 쉽게 할 수 있습니다.
let dogs = [Spot, Teddy, Tommy]
let cats = ["Mr. Humphrey Montgomery", Kitten]
[
...$dogs
Polly
...($cats | each { |elt| $"($elt) \(cat\)" })
...[Porky Bessie]
...Nemo
]
# => ╭───┬───────────────────────────────╮
# => │ 0 │ Spot │
# => │ 1 │ Teddy │
# => │ 2 │ Tommy │
# => │ 3 │ Polly │
# => │ 4 │ Mr. Humphrey Montgomery (cat) │
# => │ 5 │ Kitten (cat) │
# => │ 6 │ Porky │
# => │ 7 │ Bessie │
# => │ 8 │ ...Nemo │
# => ╰───┴───────────────────────────────╯
아래 코드는 append
를 사용한 동일한 버전입니다.
$dogs |
append Polly |
append ($cats | each { |elt| $"($elt) \(cat\)" }) |
append [Porky Bessie] |
append ...Nemo
각 append
호출은 새 목록을 생성하므로 이 두 번째 예제에서는 불필요한 중간 목록 3개가 생성됩니다. 스프레드 연산자의 경우에는 그렇지 않으므로 많은 큰 목록을 반복해서 결합하는 경우 ...
를 사용하면 (아주 약간의) 성능 이점이 있을 수 있습니다.
위 결과 목록의 마지막 항목이 "...Nemo"
라는 것을 알 수 있습니다. 이는 목록 리터럴 내부에서는 문자열이 아닌 목록을 전파하는 데만 사용할 수 있기 때문입니다. 따라서 목록 리터럴 내부에서는 변수(...$foo
), 하위 표현식(...(foo)
) 및 목록 리터럴(...[foo]
) 앞에서만 사용할 수 있습니다.
...
와 다음 표현식 사이에 공백이 있으면 스프레드 연산자로 인식되지 않습니다.
[ ... [] ]
# => ╭───┬────────────────╮
# => │ 0 │ ... │
# => │ 1 │ [list 0 items] │
# => ╰───┴────────────────╯
이는 주로 ...
가 mv ... $dir
와 같은 명령에서 스프레드 연산자와 혼동되지 않도록 하기 위한 것입니다.
레코드 리터럴에서
일부 구성 정보가 있는 레코드가 있고 이 레코드에 필드를 더 추가하고 싶다고 가정해 보겠습니다.
let config = { path: /tmp, limit: 5 }
스프레드 연산자를 사용하여 $config
의 모든 필드와 일부 새로운 추가 사항이 있는 새 레코드를 만들 수 있습니다. 단일 레코드 리터럴 내에서 여러 레코드를 스프레드할 수 있습니다.
{
...$config,
users: [alice bob],
...{ url: example.com },
...(sys mem)
}
# => ╭────────────┬───────────────╮
# => │ path │ /tmp │
# => │ limit │ 5 │
# => │ │ ╭───┬───────╮ │
# => │ users │ │ 0 │ alice │ │
# => │ │ │ 1 │ bob │ │
# => │ │ ╰───┴───────╯ │
# => │ url │ example.com │
# => │ total │ 8.3 GB │
# => │ free │ 2.6 GB │
# => │ used │ 5.7 GB │
# => │ available │ 2.6 GB │
# => │ swap total │ 2.1 GB │
# => │ swap free │ 18.0 MB │
# => │ swap used │ 2.1 GB │
# => ╰────────────┴───────────────╯
목록과 마찬가지로 레코드 리터럴 내부에서 스프레드 연산자는 변수(...$foo
), 하위 표현식(...(foo)
) 및 레코드 리터럴(...{foo:bar}
) 앞에서만 사용할 수 있습니다. 여기에서도 스프레드 연산자로 인식되려면 ...
와 다음 표현식 사이에 공백이 없어야 합니다.
명령 호출에서
나머지 매개변수가 있거나 외부 명령인 경우 명령에 인수를 전파할 수도 있습니다.
다음은 나머지 매개변수가 있는 사용자 지정 명령의 예입니다.
def foo [ --flag req opt? ...args ] { [$flag, $req, $opt, $args] | to nuon }
플래그(--flag
) 하나, 필수 위치 매개변수(req
) 하나, 선택적 위치 매개변수(opt?
) 하나, 나머지 매개변수(args
) 하나가 있습니다.
args
에 전달할 인수 목록이 있는 경우 목록 리터럴 내부에서 목록을 전파하는 것과 같은 방식으로 전파할 수 있습니다. 동일한 규칙이 적용됩니다. 스프레드 연산자는 변수, 하위 표현식 및 목록 리터럴 앞에서만 인식되며 그 사이에 공백이 허용되지 않습니다.
foo "bar" "baz" ...[1 2 3] # ...를 사용하면 숫자가 별도의 인수로 처리됩니다.
# => [false, bar, baz, [1, 2, 3]]
foo "bar" "baz" [1 2 3] # ...가 없으면 [1 2 3]이 단일 인수로 처리됩니다.
# => [false, bar, baz, [[1, 2, 3]]]
스프레드 연산자를 사용하는 더 유용한 방법은 나머지 매개변수가 있는 다른 명령이 있고 해당 인수를 foo
에 전달하려는 경우입니다.
def bar [ ...args ] { foo --flag "bar" "baz" ...$args }
bar 1 2 3
# => [true, bar, baz, [1, 2, 3]]
단일 호출에서 여러 목록을 전파하고 개별 인수를 산재시킬 수도 있습니다.
foo "bar" "baz" 1 ...[2 3] 4 5 ...(6..9 | take 2) last
# => [false, bar, baz, [1, 2, 3, 4, 5, 6, 7, last]]
플래그/명명된 인수는 일반 나머지 인수 뒤에 올 수 있는 것처럼 스프레드 인수 뒤에 올 수 있습니다.
foo "bar" "baz" 1 ...[2 3] --flag 4
# => [true, bar, baz, [1, 2, 3, 4]]
스프레드 인수가 선택적 위치 매개변수 앞에 오면 해당 선택적 매개변수는 생략된 것으로 처리됩니다.
foo "bar" ...[1 2] "not opt" # null은 opt에 대한 인수가 제공되지 않았음을 의미합니다.
# => [false, bar, null, [1, 2, "not opt"]]