The CLI answers in JSON by default, which is exact and complete, and often far more than you wanted. Two global parameters shape it.
Output formats
aws ec2 describe-availability-zones --output json
aws ec2 describe-availability-zones --output table
aws ec2 describe-availability-zones --output textjson is for programs and for reading the full structure. table draws boxes for humans. text flattens everything into tab-separated lines, one per item, which is what you want in front of wc, grep or a loop. The default comes from aws configure and can be changed per command.
Picking fields with --query
--query takes a JMESPath expression and applies it to the answer before printing.
aws ec2 describe-regions --query "Regions[0].RegionName"
aws ec2 describe-regions --query "Regions[].RegionName"
aws ec2 describe-regions --query "length(Regions)"Regions[0] is the first element; Regions[] is every element; a dotted name picks a field from each. length() counts. Nested fields chain with dots.
Filtering
aws ec2 describe-availability-zones --query "AvailabilityZones[?ZoneName=='us-east-1b'].ZoneId" --output text
aws ec2 describe-regions --query "Regions[?starts_with(RegionName, 'eu')].RegionName" --output textA filter is [?condition]. Literal strings inside it take single quotes, which is why the whole query sits in double quotes: the shell leaves it alone, and the quotes inside survive.
Shaping
aws ec2 describe-availability-zones --query "AvailabilityZones[].{Name:ZoneName,Id:ZoneId}" --output tableCurly braces build new objects with the names you choose, which makes a tidy table out of a sprawling answer.