처음 세 열을 제외하고 모두 인쇄


112

너무 번거 로움 :

awk '{print " "$4" "$5" "$6" "$7" "$8" "$9" "$10" "$11" "$12" "$13}' things

43
그냥 사용할 수없는 이유가 있나요 cut -f3-?
— Cascabel

1
@hhh 좋은 사람 .. 요약 답변의 아이디어가 마음에 듭니다.
— Chris Seymour

2
@Jefromi - 없습니다 AWK 컷과 라인 버퍼링 문제가 있기 때문에 : stackoverflow.com/questions/14360640/...
— sdaau


@Jefromi-또한 작업 cut전에 정규식 이 없으며 {}필드 구분 기호 (가변 수의 공백?)가있는 방법은 더 이상하며 수동으로 지정해야합니다. 나는 OP가 shift N존재하지 않는 명령 에 대해 듣고 싶어했다고 생각 합니다. 가장 가까운 것은 $1="";$2="";(...);print}이지만 제 경우에는 선행 공백 (아마 구분 기호)을 남깁니다.
— Tomasz Gandor 2016

답변:


50

추가 선행 또는 후행 공백을 추가하지 않는 솔루션 :

awk '{ for(i=4; i<NF; i++) printf "%s",$i OFS; if(NF) printf "%s",$NF; printf ORS}'

### Example ###
$ echo '1 2 3 4 5 6 7' |
  awk '{for(i=4;i<NF;i++)printf"%s",$i OFS;if(NF)printf"%s",$NF;printf ORS}' |
  tr ' ' '-'
4-5-6-7

Sudo_O 는 삼항 연산자를 사용하여 우아한 개선을 제안합니다.NF?ORS:OFS

$ echo '1 2 3 4 5 6 7' |
  awk '{ for(i=4; i<=NF; i++) printf "%s",$i (i==NF?ORS:OFS) }' |
  tr ' ' '-'
4-5-6-7

EdMorton 은 필드 사이의 원래 공백을 보존하는 솔루션을 제공합니다.

$ echo '1   2 3 4   5    6 7' |
  awk '{ sub(/([^ ]+ +){3}/,"") }1' |
  tr ' ' '-'
4---5----6-7

BinaryZebra 는 또한 두 가지 멋진 솔루션을 제공합니다.
(이 솔루션은 원래 문자열의 후행 공백도 보존합니다)

$ echo -e ' 1   2\t \t3     4   5   6 7 \t 8\t ' |
  awk -v n=3 '{ for ( i=1; i<=n; i++) { sub("^["FS"]*[^"FS"]+["FS"]+","",$0);} } 1 ' |
  sed 's/ /./g;s/\t/->/g;s/^/"/;s/$/"/'
"4...5...6.7.->.8->."

$ echo -e ' 1   2\t \t3     4   5   6 7 \t 8\t ' |
  awk -v n=3 '{ print gensub("["FS"]*([^"FS"]+["FS"]+){"n"}","",1); }' |
  sed 's/ /./g;s/\t/->/g;s/^/"/;s/$/"/'
"4...5...6.7.->.8->."

주석에서 larsr 에 의해 제공된 솔루션 은 거의 정확합니다.

$ echo '1 2 3 4 5 6 7' | 
  awk '{for (i=3;i<=NF;i++) $(i-2)=$i; NF=NF-2; print $0}' | tr  ' ' '-'
3-4-5-6-7

다음은 larsr 솔루션 의 고정 및 매개 변수화 된 버전입니다 .

$ echo '1 2 3 4 5 6 7' | 
  awk '{for(i=n;i<=NF;i++)$(i-(n-1))=$i;NF=NF-(n-1);print $0}' n=4 | tr ' ' '-'
4-5-6-7

2013 년 9 월 이전의 다른 모든 답변은 좋지만 공백을 추가하십시오.


EdMorton의 답변은 저에게 효과가 없었습니다 (bash 4.1.2 (1) -release, GNU Awk 3.1.7 또는 bash 3.2.25 (1) -release, GNU Awk 3.1.5). 여기서 다른 방법을 찾았 습니다 .echo ' This is a test' | awk '{print substr($0, index($0,$3))}'
— elysch

1
@elysch 아니요, 일반적으로 작동하지 않으며 특정 입력 값이 주어지면 작동하는 것처럼 보입니다. 내 답변 아래 귀하의 의견 아래에 추가 한 의견을 참조하십시오.
— Ed Morton

1
안녕하세요 @fedorqui. 내 대답이 첫 번째입니다. 내 원래 답변에서 다른 답변이 정확하지 않은 이유를 설명했습니다 (추가 선행 또는 후행 공백). 일부 사람들은 의견 내에서 개선 사항을 제안했습니다. OP에게 더 정답을 선택하도록 요청했으며 내 답변을 선택했습니다. 다른 기여자가 대답을 참조하기 위해 내 대답을 편집 한 후 (역사 참조). 당신에게 분명합니까? 내 답변의 이해도를 높이기 위해 무엇을 조언합니까? 건배 ;-)
— olibre

1
당신은 절대적으로 옳고 내 오해에 대해 매우 죄송합니다. 나는 대답을 빨리 읽었고 당신의 원래 대답을 눈치 채지 못했습니다 (예, 너무 빨리 읽었습니다). NF-1까지 반복하는 멋진 트릭을 사용하여 대답 자체에 +1을 한 다음 추가 공백을 피하기 위해 마지막 요소를 인쇄합니다. 그리고 다시 한번 죄송합니다! (향후 독자의 오해를 방지하기 위해 하루 정도 내 댓글을 삭제합니다).
— fedorqui 'SO stop harming'2016

1
나는 어떤 종류의 헤더를 사용할 것입니다 : <your answer> 그리고 그 다음에는 "다른 답변의 비교"라는 큰 제목이 뒤 따르는 수평 규칙이 있습니다. 그렇지 않으면이 비교를 다른 답변으로 옮기십시오. 사람들은 "내 코드를
— 줘

75
awk '{for(i=1;i<4;i++) $i="";print}' file

4
즉, 레코드의 선행 공백을 OFS처리하지 않으므로 선행이 남습니다 NF.
— Chris Seymour

70

컷 사용

$ cut -f4-13 file

또는 awk를 고집하고 $ 13이 마지막 필드 인 경우

$ awk '{$1=$2=$3="";print}' file

그밖에

$ awk '{for(i=4;i<=13;i++)printf "%s ",$i;printf "\n"}' file

14
마지막 예에서 "13"보다 "NF"를 사용하는 것이 좋습니다.
— glenn jackman

2
OP가 결정하는 2 가지 시나리오. 13이 마지막 필드이면 NF를 사용하는 것이 좋습니다. 그렇지 않은 경우 13을 사용하는 것이 적절합니다.
— ghostdog74 2010

3
두 번째는 $ 0 시작부터 3 개의 OFS 사본을 삭제해야합니다. 3 더 함께 할 것입니다 printf "%s ",$i당신이 여부를 알 수 없기 때문에, $i이 포함될 수 있습니다 %s등 나. 그러나 그것은 끝에 여분의 공간을 인쇄합니다.
— dubiousjim

38

이 시도:

awk '{ $1=""; $2=""; $3=""; print $0 }'

1
이것은 그것이 얼마나 역동적이기 때문에 좋습니다. 끝에 열을 추가하고 스크립트를 다시 작성할 수 없습니다.
— MinceMan

1
이것은 질문이 당신을 처리하려는 정확한 문제를 보여줍니다. 100 번째 필드에서 인쇄하는 것은 어떻습니까? 당신이 처리하지 않기 NF때문에 선두를 떠나는 것을 언급하십시오 OFS.
— Chris Seymour

24

이를 수행하는 올바른 방법은 단순히 건너 뛸 필드 수를 지정하고 나머지 필드에 대한 필드 간 간격을 유지하기 때문에 RE 간격을 사용하는 것입니다.

예를 들어이 질문에서 논의하는 것처럼 보이는 입력 형식을 고려하여 나머지 필드 사이의 간격에 영향을주지 않고 처음 3 개 필드를 건너 뛰는 것은 간단합니다.

$ echo '1   2 3 4   5    6' |
awk '{sub(/([^ ]+ +){3}/,"")}1'
4   5    6

선행 공백과 공백이 아닌 공백을 수용하고 싶지만 기본 FS를 다시 사용하려면 다음과 같습니다.

$ echo '  1   2 3 4   5    6' |
awk '{sub(/[[:space:]]*([^[:space:]]+[[:space:]]+){3}/,"")}1'
4   5    6

RE 인 FS가있는 경우 문자 집합에서 부정 할 수없는 경우 먼저 단일 문자로 변환 할 수 있습니다 (RS가 필드 내에 나타날 수 없기 때문에 단일 문자 인 경우 RS가 이상적이며 그렇지 않으면 SUBSEP를 고려하십시오). 그런 다음 RE 간격 대체를 적용한 다음 OFS로 변환합니다. 예를 들어 "."체인이 필드를 구분하는 경우 :

$ echo '1...2.3.4...5....6' |
awk -F'[.]+' '{gsub(FS,RS);sub("([^"RS"]+["RS"]+){3}","");gsub(RS,OFS)}1'
4 5 6

분명히 OFS가 단일 문자이고 입력 필드에 나타날 수없는 경우 다음과 같이 줄일 수 있습니다.

$ echo '1...2.3.4...5....6' |
awk -F'[.]+' '{gsub(FS,OFS); sub("([^"OFS"]+["OFS"]+){3}","")}1'
4 5 6

그러면 필드를 재 할당하는 모든 루프 기반 솔루션과 동일한 문제가 발생합니다. FS는 OFS로 변환됩니다. 이것이 문제라면 GNU awks의 patsplit () 함수를 살펴 봐야합니다.


나를 위해 작동하지 않았지만 (bash 4.1.2 (1) -release, GNU Awk 3.1.7 또는 bash 3.2.25 (1) -release, GNU Awk 3.1.5) 여기서 다른 방법을 찾았 습니다 .echo ' This is a test' | awk '{print substr($0, index($0,$3))}'
— elysch

2
아니요, $ 1 또는 $ 2에 $ 3이 설정된 문자열이 포함되어 있으면 실패합니다. 예를 들어 시도해 보면 $ 3 이 $ 1 의 내부 와 일치한다는 것을 echo ' That is a test' | awk '{print substr($0, index($0,$3))}'알 수 있습니다. 매우 오래된 gawk 버전에서는 플래그를 사용하여 RE 간격을 활성화해야합니다 . aaThat--re-interval
— Ed Morton

2
당신 말이 맞아요. 그건 그렇고, 정말 당신의 의견에 감사드립니다. 여러 번 "{}"와 함께 정규식을 사용하여 요소 수를 지정하고 싶었지만 사람에서 "-재 간격"을 보지 못했습니다. 당신을 위해 +1.
— elysch

1
1true 조건이므로 현재 레코드를 인쇄하는 기본 awk 작업을 호출합니다.
— Ed Morton

1
그것이 얼마나 표준인지 idk하지만 지금 대답을 추가했습니다.
— Ed Morton

10

거의 모든 답변은 현재 선행 공백, 후행 공백 또는 기타 구분 기호 문제를 추가합니다. 구분자가 공백이고 출력 구분자가 단일 공백 ​​인 네 번째 필드에서 선택하려면 다음을 사용 awk합니다.

awk '{for(i=4;i<=NF;i++)printf "%s",$i (i==NF?ORS:OFS)}' file

시작 필드를 매개 변수화하려면 다음을 수행 할 수 있습니다.

awk '{for(i=n;i<=NF;i++)printf "%s",$i (i==NF?ORS:OFS)}' n=4 file

또한 엔딩 필드 :

awk '{for(i=n;i<=m=(m>NF?NF:m);i++)printf "%s",$i (i==m?ORS:OFS)}' n=4 m=10 file

6
awk '{$1=$2=$3="";$0=$0;$1=$1}1'

입력

1 2 3 4 5 6 7

산출

4 5 6 7

4
echo 1 2 3 4 5| awk '{ for (i=3; i<=NF; i++) print $i }'

3
또는 같은 줄에 표시하려면 $ 3을 $ 1 등에 할당 한 다음 NF를 올바른 필드 수로 변경합니다. echo 1 2 3 4 5| awk '{ for (i=3; i<=NF; i++) $(i-2)=$i; NF=NF-2; print $0 }'
— larsr

안녕하세요 @larsr. 제안한 명령 줄은 하나의 정답입니다. 다른 모든 답변은 추가 공백 (선행 또는 후행)을 추가합니다. 새 답변에 명령 줄을 게시하십시오. 찬성 투표하겠습니다 ;-)
— olibre

1
안녕하세요 @sudo_O, 저는 @larsr에게 그가 그의 의견에서 제안한 명령 줄에 대해 이야기하고있었습니다. 나는 퀴 프로 코 (오해)를 알아 내기까지 약 5 분을 보냈다. 동의합니다. @Vetsin 답변 ORS은 필드 사이에 새 줄 ( )을 삽입 합니다. 당신의 이니셔티브에 대한 브라보 (나는 당신의 대답을 좋아합니다). 건배
— olibre

3

print 문 사용을 피하는 또 다른 방법 :

 $ awk '{$1=$2=$3=""}sub("^"FS"*","")' file

조건이 참일 때 awk에서 print가 기본 동작입니다.


이것은 @lhf 대답 이 가진 모든 문제를 가지고 있습니다.
— Chris Seymour

아주 좋은 생각;) 내 대답보다 낫다! (나는 이미 작년에 귀하의 답변을 upvoted했습니다) Cheers
— olibre

awk '{$1=$2=$3=""}sub("^"OFS"+","")' file$ 1, $ 2 및 $ 3 내용을 변경 한 후 남은 OFS는 다음 과 같아야합니다 .

3

아무도 일반 쉘을 제공하지 않았다는 것을 믿을 수 없습니다.

while read -r a b c d; do echo "$d"; done < file

비슷한 솔루션의 경우 +1 ... 그러나이 file크기가 크면 (> 10-30KiB) 성능 문제가있을 수 있습니다 . 대용량 파일의 경우 awk솔루션이 더 잘 수행됩니다.
— TrueY

3

옵션 1 ~ 3에는 다중 공백 문제가 있습니다 (단순함). 이것이 문제없이 여러 개의 공백을 처리하는 옵션 4와 5를 개발 한 이유입니다. 물론, 옵션 4 또는 5가 n=0둘 다 와 함께 사용 되면 n=0분할이 없음 을 의미 하므로 선행 공백이 유지됩니다 .

옵션 1

단순 절단 솔루션 (단일 구분 기호로 작동) :

$ echo '1 2 3 4 5 6 7 8' | cut -d' ' -f4-
4 5 6 7 8

옵션 2

awk 재 계산을 강제하면 때때로 추가 된 선행 공백의 문제 (일부 버전의 awk에서 작동)가 해결됩니다.

$ echo '1 2 3 4 5 6 7 8' | awk '{ $1=$2=$3="";$0=$0;} NF=NF'
4 5 6 7 8

옵션 3

형식이 지정된 각 필드를 인쇄 printf하면 더 많은 제어가 가능합니다.

$ echo '    1    2  3     4   5   6 7     8  ' |
  awk -v n=3 '{ for (i=n+1; i<=NF; i++){printf("%s%s",$i,i==NF?RS:OFS);} }'
4 5 6 7 8

그러나 이전의 모든 답변은 필드 간의 모든 FS를 OFS로 변경합니다. 이에 대한 몇 가지 솔루션을 구축해 보겠습니다.

옵션 4

필드와 구분 기호를 제거하는 sub가있는 루프는 더 이식성이 뛰어나며 FS를 OFS로 변경하지 않습니다.

$ echo '    1    2  3     4   5   6 7     8  ' |
awk -v n=3 '{ for(i=1;i<=n;i++) { sub("^["FS"]*[^"FS"]+["FS"]+","",$0);} } 1 '
4   5   6 7     8

노트: "^ ["FS "] *"는 선행 공백이있는 입력을 허용합니다.

옵션 5

추가 선행 또는 후행 공백을 추가하지 않고 gensubGNU awk 의 함수 를 사용하여 기존 공백을 유지하는 솔루션을 구축하는 것이 가능합니다 .

$ echo '    1    2  3     4   5   6 7     8  ' |
awk -v n=3 '{ print gensub("["FS"]*([^"FS"]+["FS"]+){"n"}","",1); }'
4   5   6 7     8 

또한 개수가 주어진 필드 목록을 교체하는 데 사용할 수도 있습니다 n.

$ echo '    1    2  3     4   5   6 7     8  ' |
  awk -v n=3 '{ a=gensub("["FS"]*([^"FS"]+["FS"]+){"n"}","",1);
                b=gensub("^(.*)("a")","\\1",1);
                print "|"a"|","!"b"!";
               }'
|4   5   6 7     8  | !    1    2  3     !

물론 이러한 경우 OFS는 줄의 두 부분을 구분하는 데 사용되며 필드의 후행 공백은 여전히 ​​인쇄됩니다.

참고 1 : ["FS"]* 입력 줄에 선행 공백을 허용하는 데 사용됩니다.


안녕하세요 BZ 귀하의 답변은 좋습니다. 그러나 옵션 3은 공백 (예 :)으로 시작하는 문자열에서 작동하지 않습니다 " 1 2 3 4 5 6 7 8 ". 옵션 4는 좋지만 공백으로 시작하는 문자열을 사용하여 선행 공백을 남겨 둡니다. 이것이 고칠 수 있다고 생각하십니까? 명령 echo " 1 2 3 4 5 6 7 8 " | your awk script | sed 's/ /./g;s/\t/->/g;s/^/"/;s/$/"/'을 사용 하여 선행 / 중간 / 후행 공백을 확인할 수 있습니다 ... Cheers;)
— olibre

안녕하세요 @olibre. 옵션 3이 공백으로 인해 실패한다는 것이 옵션 4와 5를 개발하는 이유입니다. 옵션 4는 입력에 공백이 있고 n이 0 (n = 0)으로 설정된 경우에만 선행 공백을 남깁니다 . 필드 선택이 없을 때 정답이라고 생각합니다 (IMO를 수정할 것이 없음). 건배.

괜찮아. 덕분에 추가 정보를 :-) 이러한 추가 정보를 원하시면 :-) 건배 제공하는 답변을 향상하십시오
— olibre

Perfect :-) 귀하의 사용자가 비활성화
— 되어서

1

Cut에는 열을 쉽고 빠르게 삭제할 수있는 --complement 플래그가 있습니다. 결과 구문은 수행하려는 작업과 유사하므로 솔루션을 더 쉽게 읽고 이해할 수 있습니다. 연속되지 않은 열을 삭제하려는 경우에도 보완이 작동합니다.

$ foo='1 2 3 %s 5 6 7'
$ echo "$foo" | cut --complement -d' ' -f1-3
%s 5 6 7
$

더 많은 답변을 설명해 주시겠습니까?
— Zulu

위의 편집 내용이 이해에 도움이됩니까? 요점은 컷의 보완 플래그를 사용하는 것입니다. 솔루션은 AWK 또는 perl 기반 솔루션보다 더 빠르고 간결한 구현이어야합니다. 또한 임의의 열을자를 수 있습니다.
— Michael Back

1

선행 또는 후행 공백을 추가하지 않는 Perl 솔루션 :

perl -lane 'splice @F,0,3; print join " ",@F' file

perl @Fautosplit 배열은 인덱스에서 시작하고 0awk 필드는 다음으로 시작합니다.$1


쉼표로 구분 된 데이터를위한 Perl 솔루션 :

perl -F, -lane 'splice @F,0,3; print join ",",@F' file

Python 솔루션 :

python -c "import sys;[sys.stdout.write(' '.join(line.split()[3:]) + '\n') for line in sys.stdin]" < file


0

저에게 가장 컴팩트하고 규정을 준수하는 솔루션은

$ a='1   2\t \t3     4   5   6 7 \t 8\t '; 
$ echo -e "$a" | awk -v n=3 '{while (i<n) {i++; sub($1 FS"*", "")}; print $0}'

예를 들어 foo.txt 파일처럼 처리 할 줄이 더 있다면 i를 0으로 재설정하는 것을 잊지 마십시오.

$ awk -v n=3 '{i=0; while (i<n) {i++; sub($1 FS"*", "")}; print $0}' foo.txt

포럼에 감사드립니다.


0

나는 처음으로 크게 찬성했지만 잘못된 답변에 짜증이 나서 거기에 답글을 쓸만큼 충분히 찾았고 여기에 잘못된 답변이 표시되어 있습니다. 답을 그렇게 복잡하게 만들 이유가 없기 때문에 제안 된 해결책이 마음에 들지 않습니다.

IP 주소가있는 $ 5 이후에 더 많은 텍스트가 있거나 텍스트가 없을 수있는 로그가 있습니다. $ 5 이후에 아무것도 없으면 IP 주소에서 줄 끝까지 모든 것이 필요합니다. 제 경우에는 awk oneliner가 아니라 awk 프로그램이있는 경우 실제로 awk가 문제를 해결해야합니다. 예전 멋지고 가장 많이 찬성했지만 완전히 잘못된 대답을 사용하여 처음 4 개의 필드를 제거하려고 할 때 :

echo "  7 27.10.16. Thu 11:57:18 37.244.182.218 one two three" | awk '{$1=$2=$3=$4=""; printf "[%s]\n", $0}'

그것은 잘못되고 쓸모없는 응답을 뱉어냅니다 (시연하기 위해 []를 추가했습니다).

[    37.244.182.218 one two three]

대신, 절단 점과 awk가 필요할 때까지 열이 고정 너비 인 경우 정확하고 간단한 대답은 다음과 같습니다.

echo "  7 27.10.16. Thu 11:57:18 37.244.182.218 one two three" | awk '{printf "[%s]\n", substr($0,28)}'

원하는 출력을 생성합니다.

[37.244.182.218 one two three]

0

이 다른 가능성을 찾았습니다. 아마도 유용 할 수도 있습니다.

awk 'BEGIN {OFS=ORS="\t" }; {for(i=1; i<14; i++) print $i " "; print $NF "\n" }' your_file

참고 : 1. 테이블 형식 데이터 및 $ 1부터 $ 14까지


0

컷 사용 :

cut -d <The character between characters> -f <number of first column>,<number of last column> <file name>

예 : 다음을 file1포함하는 경우 :car.is.nice.equal.bmw

실행 : cut -d . -f1,3 file1 인쇄합니다car.is.nice


귀하의 솔루션이 거꾸로 된 것 같습니다. 질문 제목 검토하십시오 처음 세 열을 모두 * 인쇄하지만 *
— 스테판 Crain

-1

이것은 이전 답변 중 일부와 그리 멀지 않지만 몇 가지 문제를 해결합니다.

cols.sh:

#!/bin/bash
awk -v s=$1 '{for(i=s; i<=NF;i++) printf "%-5s", $i; print "" }'

이제 시작 열이 될 인수로 호출 할 수 있습니다.

$ echo "1 2 3 4 5 6 7 8 9 10 11 12 13 14" | ./cols.sh 3 
3    4    5    6    7    8    9    10   11   12   13   14

또는:

$ echo "1 2 3 4 5 6 7 8 9 10 11 12 13 14" | ./cols.sh 7 
7    8    9    10   11   12   13   14

이것은 1 색인입니다. 제로 인덱스를 선호하는 경우 i=s + 1대신 사용하십시오.

또한 시작 색인 과 끝 색인에 대한 인수가 필요 하면 파일을 다음과 같이 변경하십시오.

#!/bin/bash
awk -v s=$1 -v e=$2 '{for(i=s; i<=e;i++) printf "%-5s", $i; print "" }'

예를 들면 :

$ echo "1 2 3 4 5 6 7 8 9 10 11 12 13 14" | ./cols.sh 7 9 
7    8    9

%-5s가지런 5 문자 폭 열로 결과; 이것이 충분하지 않으면 숫자를 늘리거나 %s정렬에 관심이 없으면 대신 공백을 사용하십시오.


-1

% 문제를 방지하고 인쇄 할 열이 4 개 미만인 경우 아무것도 반환하지 않는다는 점에서 고유 한 AWK printf 기반 솔루션 :

awk 'NF > 3 { for(i=4; i<NF; i++) printf("%s ", $(i)); print $(i) }'

테스트 :

$ x='1 2 3 %s 4 5 6'
$ echo "$x" | awk 'NF > 3 { for(i=4; i<NF; i++) printf("%s ", $(i)); print $(i) }'
%s 4 5 6
$ x='1 2 3'
$ echo "$x" | awk 'NF > 3 { for(i=4; i<NF; i++) printf("%s ", $(i)); print $(i) }'
$ x='1 2 3 '
$ echo "$x" | awk 'NF > 3 { for(i=4; i<NF; i++) printf("%s ", $(i)); print $(i) }'
$
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.